如何为i18n构建验证错误代码

时间:2017-03-20 13:48:03

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

我正在使用Spring MVC框架编写REST-API 我正在使用bean验证,如:

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

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

@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]

现在我需要使用将由不同UI解析的语言无关error code来实现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并添加自定义(开销)来构建正确的消息

1 个答案:

答案 0 :(得分: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.

使用此功能,您无需在注释中编写任何消息代码。 希望这会有所帮助。