【问题标题】:how to build validation error code for i18n如何为 i18n 构建验证错误代码
【发布时间】:2017-03-20 13:48:03
【问题描述】:

我正在使用 Spring MVC 框架编写 REST-API。
我正在使用 bean-validation,例如:

class Person {
    @NotNull
    String name;
    @NotNull
    String email;
    @Min(0)
    Integer age;
}

我在控制器中使用@Valid 注释验证Person

@PostMapping
public Person create(@Valid @RequestBody Person person) {return ...;}

为了使错误易于阅读,我使用了 Spring 的顶级错误处理程序:

@ControllerAdvice
public class CustomExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody String handle(MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult().getFieldErrors().stream()
                .map(this::buildMessage)
                .collect(Collectors.toList());
        return errors.toString();
    }

    private String buildMessage(FieldError fe) {
        return fe.getField() + " " + fe.getDefaultMessage();
    }
}

所以我的错误看起来像:[name may not be null, email may not be null]

现在我需要使用独立于语言的error code,它将被不同的 UI 解析来实现 i18n。
有没有办法构建完整的错误代码?(包含字段名称)

我看到以下解决方案:

  1. 每次使用注解时都使用自定义消息(丑):

    class Person {
        @NotNull(message="app.error.person.name.not.null")
        String name;
        @NotNull(message="app.error.person.email.not.null")
        String email;
        @Min(0)(message="app.error.person.age.below.zero")
        Integer age;
    }
    
  2. 在我的异常处理程序中构建正确的代码(不知道如何):

    private String buildMessage(FieldError fe) {
        return "app.error." +
                fe.getObjectName() + "." +
                fe.getField() + "." +
                fe.getDefaultMessage().replaceAll("\\s", "");//don't know how to connect to concrete annotation
    }
    

    所以消息会像app.error.person.name.maynotbenull

  3. 通过删除默认的 ConstraintViolation 并添加自定义(开销),为它们重写所有注释和验证器以构建正确的消息

【问题讨论】:

    标签: java spring spring-mvc internationalization bean-validation


    【解决方案1】:

    无需在注解中指定消息。这将是一个开销

    @ControllerAdvice
    public class CustomExceptionHandler {
    
      @Autowired
      MessageSource messageSource;
    
      @ExceptionHandler(MethodArgumentNotValidException.class)
      @ResponseBody String handle(MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult().getFieldErrors().stream()
                .map(this::buildMessage)
                .collect(Collectors.toList());
        return errors.toString();
      }
    
      private String buildMessage(FieldError fe) {
        StringBuilder errorCode = new StringBuilder("");
        String localizedErrorMsg = "";
        errorCode.append("error").append(".");
        errorCode.append(fe.getObjectName()).append(".");
        errorCode.append(fe.getField()).append(".");
        errorCode.append(fe.getCode().toLowerCase());
    
            try {
                localizedErrorMsg = this.messageSource.getMessage(errorCode,(Object[]) null, LocaleContextHolder.getLocale());
            } catch (Exception ex) {
                localizedErrorMsg = fe.getDefaultMessage();
            }
        return localizedErrorMsg;
      }
    }
    

    并且在消息文件(i18n)中使用以下格式

    error.person.name.notnull = Name must not be null
    error.person.email.notnull = Email must not be null
    error.person.age.min= Minimum age should greater than 0.
    

    使用它,您不必在注释中编写任何消息代码。 希望这会有所帮助。

    【讨论】:

    • 我不需要本地化我的消息,但是好的,我可以扔掉 try 块。
    • 我想问一下fe.getCode()会在什么情况下返回null
    • fe.getCode() 永远不会返回 null。 MethodArgumentNotValidException 只会因为该代码而被抛出
    • 但我们有 FieldError 的构造函数,将 codes 设置为 null
    • 在 FiledError 2 构造函数中可用。如果存在注释,它将调用第二个设置代码值的注释
    猜你喜欢
    • 1970-01-01
    • 2022-01-13
    • 2016-03-27
    • 2011-05-11
    • 2018-11-11
    • 1970-01-01
    • 1970-01-01
    • 2011-05-22
    • 1970-01-01
    相关资源
    最近更新 更多