背景 在一个完整的项目里面,肯定是有各种各样的入参校验的,如果业务上的一些逻辑校验,可以放在 Service 层面进行,但是如果是 Controller 里面的校验,直接可以用 validation 进行验证。配合注解可以很方便的实现各种各样的入参校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public  class  User      @NotBlank(message = "用户名称不能为空")      private  String name;     @Max(value = 18)      @Min(value = 10)      private  Integer age;     public  String getName ()           return  name;     }     public  void  setName (String name)           this .name = name;     }     public  Integer getAge ()           return  age;     }     public  void  setAge (Integer age)           this .age = age;     } } 
然后在Controller里面
1 2 3 4 5 6 7 8 9 @PostMapping(value = "test") public  String testError (@Valid   @RequestBody  User user ,Errors errors)     if (errors.hasErrors()){         for  (ObjectError err : errors.getAllErrors()) {             return  err.getDefaultMessage();         }     }     return  "OK" ; } 
当入参的 JSON 如果 name 是空的话,就会直接返回 用户名称不能为空,当然这里是做了一些简化,返回的应该还有 Code 和 Message。这样的话一个简单的 Controller 检验入参就实现的,但是为了扩展性和可维护性,还需要考虑国际化以及可配置化。
扩展性 如果在之后需要更改一个校验的提示语,那么在上面的代码里面是需要修改代码的,那么最好的办法就是把这些提示信息都加到配置文件中,那么以后需要修改某些提示的话,直接改配置文件即可。4.7.1  中有一个类 MessageCodesResolver 可以用来实现错误信息的可配置化
MessageCodesResolver 这个类需要和spring.mvc.message-codes-resolver-format一起来使用,根据官方文档的提示,查看DefaultMessageCodesResolver.Format,会发现这个参数有两个枚举,一个是 PREFIX_ERROR_CODE,另一个是 POSTFIX_ERROR_CODE。
PREFIX_ERROR_CODE
NotBlank.user.name = 用户名称不能为空
 
 
POSTFIX_ERROR_CODE。
user.name.NotBlank = 用户名称不能为空
 
 
 
个人偏向于第二种写法的,因为感觉第二种更加符合阅读习惯。
messgae配置文件 既然需要做成配置文件,那么在 application.yml 里面把一些属性都配置好,如下:
1 2 3 4 5 spring:   mvc:      message-codes-resolver-format:  postfix_error_code    messages:      basename:  i18n/validation  
然后在 Resources 下面新建一个 validation.properties
1 2 3 user.name.NotBlank = 用户名称不能为空 user.age.Max = 年龄最高不能高于18  user.age.Min = 年龄最高不能低于10  
Controller 校验 1 2 3 4 5 6 7 8 9 10 11 12 13 @Autowired MessageSource messageSource; @PostMapping(value = "test/cn") public  String testErrorCn (@Valid   @RequestBody  User user ,Errors errors)     if (errors.hasErrors()){         for  (ObjectError err : errors.getAllErrors()) {             String msg = messageSource.getMessage(err, Locale.CHINA);             return  msg;         }     }     return  "OK" ; } 
在这里暂时先不管 Locale.CHINA 这个参数的含义,首先通过 errors 先判断入参是否有问题,有的话直接通过 messageSource.getMessage() 方法直接返回错误信息即可。
PsotMan测试 1 2 3 4 POST /test/cn HTTP/1.1  Host : localhost:8071 {"age" :15 } 
返回:用户名称不能为空
那么以后如果需要修改返回提示的话,直接修改配置文件即可,从而不需要修改代码。
国际化 如果需要将该提示国际化,直接修改配置文件即可,新建两个配置文件validation_zh_CN.properties,validation_en_US.properties,然后分别新建提示配置如下:
1 2 3 4 5 6 7 8 9 validation_zh_CN.properties: user.name.NotBlank  = 用户名称不能为空 user.age.Max  = 年龄最高不能高于18 user.age.Min  = 年龄最高不能低于10 validation_en_US.properties: user.name.NotBlank  = user name can not be null user.age.Max  = the max gae can not grater than 18 user.age.Min  = the max gae can not less than 10 
此时在Controller里面代如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @RestController @ResponseBody public  class  StartController      @Autowired      MessageSource messageSource;     @PostMapping(value = "test")      public  String testError (@Valid   @RequestBody  User user ,Errors errors)          if (errors.hasErrors()){             for  (ObjectError err : errors.getAllErrors()) {                 return  err.getDefaultMessage();             }         }         return  "OK" ;     }     @PostMapping(value = "test/cn")      public  String testErrorCn (@Valid   @RequestBody  User user ,Errors errors)          if (errors.hasErrors()){             for  (ObjectError err : errors.getAllErrors()) {                 String msg = messageSource.getMessage(err, Locale.CHINA);                 return  msg;             }         }         return  "OK" ;     }     @PostMapping(value = "test/en")      public  String testErrorEn (@Valid   @RequestBody  User user ,Errors errors)          if (errors.hasErrors()){             for  (ObjectError err : errors.getAllErrors()) {                 String msg = messageSource.getMessage(err, Locale.US);                 return  msg;             }         }         return  "OK" ;     } } 
User的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public  class  User      @NotBlank      private  String name;     @Max(value = 18)      @Min(value = 10)      private  Integer age;     public  String getName ()           return  name;     }     public  void  setName (String name)           this .name = name;     }     public  Integer getAge ()           return  age;     }     public  void  setAge (Integer age)           this .age = age;     } } 
当我们请求 test/en 和 test/cn的时候就会出现不同的提示,
test/en
user name can not be null
 
 
test/cn
用户名称不能为空
 
 
 
这其中重要的实现就是 Locale.US 和 Locale.CHINA,在这里实现的话,最好是根据用户的选择语言来动态的切换实现Local的转换。