REST API 使用 JAX-RS 同时在 JSON 数组中发送多个错误

REST API Send multiples errors in a JSON Array at the same time with JAX-RS

我正在学习如何使用 Java EE 和 JAX-RS 创建 API。但是我在网上找不到任何关于我的问题的答案。

我有一条“注册”路线:

    @POST
    @Path("/signup")
    @Produces(MediaType.APPLICATION_JSON)
    public Response register(@FormParam("email") String email, @FormParam("pseudo") String pseudo, @FormParam("password") String password,@FormParam("firstname") String firstname) {
                List<HashMap<String, String>> errorMsgs = authManager.register(email, pseudo, password, firstname, lastname);

        if (errorMsgs.isEmpty()) { // Si la liste est vide, il n'y a pas eu d'erreur
            HashMap<String, String> success = new HashMap<>();
            success.put("title", "Inscription réussie");
            success.put("message", "L'utilisateur a été ajouté en base de données.");
            return Response.ok().entity(success).build();
        } else {
            HashMap<String, List<HashMap<String, String>>> errors = new HashMap<>();
            errors.put("errors", errorMsgs);

            return Response.status(422).entity(errors).build();
        }
    }

目前,要发送我需要的所有错误消息,我使用的是:

public List<HashMap<String, String>> register(String email, String pseudo, String password, String firstname, String lastname) {
        List<HashMap<String, String>> errors = new ArrayList<>();

        errors.addAll(checkEmail(email));
        errors.addAll(checkPseudo(pseudo));
        errors.addAll(checkPassword(password));
        errors.addAll(checkFirstname(firstname));
        errors.addAll(checkLastname(lastname));

        if (errors.isEmpty()) {
            userDAO.create(new User(pseudo, email, password, firstname, lastname));
        }

        return errors;
    }

    private List<HashMap<String, String>> checkEmail(String email) {
        List<HashMap<String, String>> errors = new ArrayList<>();

        if (email != null && !email.isEmpty()) {
            if (email.matches("([^.@]+)(\.[^.@]+)*@([^.@]+\.)+([^.@]+)")) {
                Optional<User> user = userDAO.findByEmail(email);
                if(user.isPresent()) {
                    HashMap<String, String> error = new HashMap<>();
                    error.put("code", CODE_FIELD_ALREADY_EXIST);
                    error.put("field", "email");
                    error.put("title", "L'adresse email existe déjà dans la base de données.");
                    error.put("message", "Essayez de choisir une autre adresse email qui n'est pas déjà utilisé.");

                    errors.add(error);
                }
            } else {
                HashMap<String, String> error = new HashMap<>();
                error.put("code", CODE_BAD_FORMAT);
                error.put("field", "email");
                error.put("title", "L'adresse email n'est pas au bon format.");
                error.put("message", "Vous devez entrer un email au format valide (exemple@domaine.fr)");
                errors.add(error);
            }
        } else {
            HashMap<String, String> error = new HashMap<>();
            error.put("code", CODE_EMPTY_FIELD);
            error.put("field", "email");
            error.put("title", "Le champ email est vide.");
            error.put("message", "L'adresse email est obligatoire.");
            errors.add(error);
        }

        return errors;
    }

    private List<HashMap<String, String>> checkPseudo(String pseudo) {
        List<HashMap<String, String>> errors = new ArrayList<>();

        if (pseudo != null && !pseudo.isEmpty()) {
            if (pseudo.length() > 50) {
                HashMap<String, String> error = new HashMap<>();
                error.put("code", CODE_TOO_LONG_FIELD);
                error.put("field", "pseudo");
                error.put("title", "Le pseudo est trop long.");
                error.put("message", "Votre pseudo ne peut pas faire plus de 50 caractères.");

                errors.add(error);
            } else {
                if (userDAO.findByPseudo(pseudo).isPresent()) {
                    HashMap<String, String> error = new HashMap<>();
                    error.put("code", CODE_FIELD_ALREADY_EXIST);
                    error.put("field", "pseudo");
                    error.put("title", "Le pseudo " + pseudo + " existe déjà dans la base de données.");
                    error.put("message", "Essayez de choisir un pseudo qui n'est pas déjà utilisé.");

                    errors.add(error);
                }
            }
        } else {
            HashMap<String, String> error = new HashMap<>();
            error.put("code", CODE_EMPTY_FIELD);
            error.put("field", "pseudo");
            error.put("title", "Le champ pseudo est vide");
            error.put("message", "Le pseudo est obligatoire.");
            errors.add(error);
        }

        return errors;
    }

    private List<HashMap<String, String>> checkPassword(String password) {
        List<HashMap<String, String>> errors = new ArrayList<>();

        if (password != null && !password.isEmpty()) {
            if (password.length() < 8) {
                HashMap<String, String> error = new HashMap<>();
                error.put("code", CODE_TOO_SHORT_FIELD);
                error.put("field", "password");
                error.put("title", "Le mot de passe est trop court.");
                error.put("message", "Votre mot de passe doit faire au minimum 8 caractères.");

                errors.add(error);
            }
        } else {
            HashMap<String, String> error = new HashMap<>();
            error.put("code", CODE_EMPTY_FIELD);
            error.put("field", "password");
            error.put("title", "Le champ est vide");
            error.put("message", "Le mot de passe est obligatoire.");

            errors.add(error);
        }

        return errors;
    }

    private List<HashMap<String, String>> checkFirstname(String firstname) {
        List<HashMap<String, String>> errors = new ArrayList<>();

        if (firstname != null && !firstname.isEmpty()) {
            if (firstname.length() > 50) {
                HashMap<String, String> error = new HashMap<>();
                error.put("code", CODE_TOO_LONG_FIELD);
                error.put("field", "firstname");
                error.put("title", "Le prénom est trop long");
                error.put("message", "Votre prénom doit avoir un maximum de 50 caractères.");

                errors.add(error);
            }
        } else {
            HashMap<String, String> error = new HashMap<>();
            error.put("code", CODE_EMPTY_FIELD);
            error.put("field", "firstname");
            error.put("title", "Le champ est vide");
            error.put("message", "Le prénom est obligatoire.");

            errors.add(error);
        }

        return errors;
    }

    private List<HashMap<String, String>> checkLastname(String lastname) {
        List<HashMap<String, String>> errors = new ArrayList<>();

        if (lastname != null && !lastname.isEmpty()) {
            if (lastname.length() > 50) {
                HashMap<String, String> error = new HashMap<>();
                error.put("code", CODE_TOO_LONG_FIELD);
                error.put("field", "lastname");
                error.put("title", "Le nom est trop long");
                error.put("message", "Votre nom doit avoir un maximum de 50 caractères.");

                errors.add(error);
            }
        } else {
            HashMap<String, String> error = new HashMap<>();
            error.put("code", CODE_EMPTY_FIELD);
            error.put("field", "lastname");
            error.put("title", "Le champ est vide");
            error.put("message", "Le nom est obligatoire.");

            errors.add(error);
        }

        return errors;
    }
}

这段代码基本上向我发送了这种类型的响应:

这就是我想要的,但是代码真的很丑,而且会变得很乱,我也觉得这不是一个好的实践。所以我在网上寻找更好的方法。 我发现有些人使用异常处理,使用“ExceptionMapper”或 WebApplicationException,但问题是它一次只能处理一个问题,所以,这不是我需要的,因为我想发送多个错误一次回应。 所以我想知道你们中有没有人对此有解决方案,因为我没有找到任何关于我的问题的信息:| !

谢谢 :) !

您可以采用以下两种错误处理方法,使其更易于理解

1.使用 javax.validation

您可以修改 JAX-RS 端点以接受 java class 并让 javax.validation 为您处理验证。

这就是您的 JAX-RS 端点的样子:

 @POST
    @Path("/signup")
    @Produces(MediaType.APPLICATION_JSON)
    public Response register(@Valid RegisterationOptions registrationOptions) {

      ...
}

RegistrationOptions.java class:

public class RegistrationOptions {

@NotNull
String email; 

@NotNull
String pseudo;

@NotNull
String password;

@NotNull
String firstname;

 .....


}

有多个注释可用于验证。例如。 @Min, @Max, @Negative, @Size, @Email, @Future.

它们都接受一个 message 参数,如果验证不成功,您可以在其中记录自定义消息。

不过请记住,您需要创建一个 Excpetion Mapper 来将抛出的验证异常映射到有效的 JAX-RS 响应。

据我所知,对于任何不满足您设置的条件的值,这将引发异常。它不会为每次验证抛出异常。但我可能错了。

2。为错误消息使用自定义 class

如果您想向用户显示多个错误,为您的错误对象创建 POJO 可能比创建 HashMap 列表更清晰、更容易。

一个例子是:

public class RegistrationError {
  private enum Field {
    email,password,pseudo,firstname;
    
    String getFieldFullName() {
      switch (this) {
        case email:
          return "Email Address";
        case password:
          return "Password";
        case pseudo:
          return "Pseudo";
        case firstname:
          return "First Name";
      }
    }
  }
  private enum ErrorCodes {
    FIELD_ALREADY_EXIST,EMPTY_FIELD, TOO_LONG_FIELD,
  }
  
  private final String code;
  private final String field;
  private final String title;
  private final String message;
  
  private RegistrationError(String code, String field, String title, String message) {
    this.code = code;
    this.field = field;
    this.title = title;
    this.message = message;
  }
  
  public static RegistrationError generateError(ErrorCodes errorCodes, Field field){
    final String errorTitle = RegistrationError.getErrorTitle(errorCodes, field);
    final String errorMessage = RegistrationError.getErrorMessage(errorCodes, field);
    return new RegistrationError(errorCodes.name(), field.name(), errorTitle, errorMessage);
  }
  
  public static String getErrorTitle(ErrorCodes errorCodes, Field field) {
    switch (errorCodes) {
      case FIELD_ALREADY_EXIST:
        return field.getFieldFullName() + " already exists in DB";
      case EMPTY_FIELD:
        return field.getFieldFullName() + " cannot be empty";
      case TOO_LONG_FIELD:
        return field.getFieldFullName() + " is too long";
      default:
        return field.getFieldFullName() + " is invalid";
    }
  }
  
  public static String getErrorMessage(ErrorCodes errorCodes, Field field) {
    switch (errorCodes) {
      case FIELD_ALREADY_EXIST:
        return field.getFieldFullName() + " already exists in DB";
      case EMPTY_FIELD:
        return field.getFieldFullName() + " cannot be empty";
      case TOO_LONG_FIELD:
        return field.getFieldFullName() + " is too long";
      default:
        return field.getFieldFullName() + " is invalid";
    }
  }
  
  public String getCode() {
    return code;
  }
  
  public String getField() {
    return field;
  }
  
  public String getTitle() {
    return title;
  }
  
  public String getMessage() {
    return message;
  }
}

然后您可以在验证字段的方法中使用它:

public List<RegistrationError> register(String email, String pseudo, String password, String firstname, String lastname) {
    List<RegistrationError> errors = new ArrayList<>();
    
    errors.addAll(checkEmail(email));
    errors.addAll(checkPseudo(pseudo));
    errors.addAll(checkPassword(password));
    errors.addAll(checkFirstname(firstname));
    errors.addAll(checkLastname(lastname));
    
    if (errors.isEmpty()) {
      userDAO.create(new User(pseudo, email, password, firstname, lastname));
    }
    
    return errors;
  }
  
  private List<RegistrationError> checkEmail(String email) {
    List<RegistrationError> errors = new ArrayList<>();
    
    if (email != null && !email.isEmpty()) {
      if (email.matches("([^.@]+)(\.[^.@]+)*@([^.@]+\.)+([^.@]+)")) {
        Optional<User> user = userDAO.findByEmail(email);
        if(user.isPresent()) {
          errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.FIELD_ALREADY_EXIST, RegistrationError.Field.email));
        }
      } else {
        errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.EMPTY_FIELD, RegistrationError.Field.email));
      }
    } else {
      errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.EMPTY_FIELD, RegistrationError.Field.email));
    }
    
    return errors;
  }
  
  private List<RegistrationError> checkPseudo(String pseudo) {
    List<RegistrationError> errors = new ArrayList<>();
    
    if (pseudo != null && !pseudo.isEmpty()) {
      if (pseudo.length() > 50) {
        errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.EMPTY_FIELD, RegistrationError.Field.pseudo));
      } else {
        if (userDAO.findByPseudo(pseudo).isPresent()) {
          errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.FIELD_ALREADY_EXIST, RegistrationError.Field.email));
        }
      }
    } else {
      errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.EMPTY_FIELD, RegistrationError.Field.email));
    }
    
    return errors;
  }
  
  private List<RegistrationError> checkPassword(String password) {
    List<RegistrationError> errors = new ArrayList<>();
    
    if (password != null && !password.isEmpty()) {
      if (password.length() < 8) {
        errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.EMPTY_FIELD, RegistrationError.Field.email));
      }
    } else {
      errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.EMPTY_FIELD, RegistrationError.Field.email));
    }
    
    return errors;
  }
  
  private List<RegistrationError> checkFirstname(String firstname) {
    List<RegistrationError> errors = new ArrayList<>();
    
    if (firstname != null && !firstname.isEmpty()) {
      if (firstname.length() > 50) {
        errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.EMPTY_FIELD, RegistrationError.Field.email));
      }
    } else {
      errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.EMPTY_FIELD, RegistrationError.Field.email));
    }
    
    return errors;
  }
  
  private List<RegistrationError> checkLastname(String lastname) {
    List<RegistrationError> errors = new ArrayList<>();
    
    if (lastname != null && !lastname.isEmpty()) {
      if (lastname.length() > 50) {
        errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.EMPTY_FIELD, RegistrationError.Field.email));
      }
    } else {
      errors.add(RegistrationError.generateError(RegistrationError.ErrorCodes.EMPTY_FIELD, RegistrationError.Field.email));
    }
    
    return errors;
  }

(使用前需要更正错误和字段)

并且您的 JAX-RS 方法可以将此数组发送为:

List<RegistrationError> errorMsgs = authManager.register(email, pseudo, password, firstname, lastname);

...

            HashMap<String, List<RegistrationErrors>> errors = new HashMap<>();
            errors.put("errors", errorMsgs);

            return Response.status(422).entity(errors).build();

如果您构建异常映射器,您还可以将此方法与 javax.validation 包方法结合使用。