最近开始由原来的 Android 工程师 Java web 向转型了, 废话不多说, 上图
背景
JSR 303 - Bean Validation 是一个数据验证的规范, 2009 年 11 月确定最终方案. Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 我们通常写接口时会对传来的参数进行校验判断, 比如字符串非空判断, 值在多少返回等, 这些就要用到 Spring 的参数校验, 这里我们使用在 spring-boot-starter-web 包里面有 hibernate-validator 包, 参数校验有几种方式, 如下
1. 使用 @Valid+BindingResult
2. 注解使用 @Valid + 全局异常捕捉处理
下面来介绍这两种方式的使用
使用 @Valid+BindingResult 参数验证
1. 符合国际 JSP 303 规范, 先定义个 Bean, 别人在字段名上添加验证的注解,@NotBlank 字段名不能为 null, 同时长度大于 0 常有的校验类型有
- Constraint 详细信息
- @Null 被注释的元素必须为 null
- @NotNull 被注释的元素必须不为 null
- @AssertTrue 被注释的元素必须为 true
- @AssertFalse 被注释的元素必须为 false
- @Min(value) 被注释的元素必须是一个数字, 其值必须大于等于指定的最小值
- @Max(value) 被注释的元素必须是一个数字, 其值必须小于等于指定的最大值
- @DecimalMin(value) 被注释的元素必须是一个数字, 其值必须大于等于指定的最小值
- @DecimalMax(value) 被注释的元素必须是一个数字, 其值必须小于等于指定的最大值
- @Size(max, min) 被注释的元素的大小必须在指定的范围内
- @Digits (integer, fraction) 被注释的元素必须是一个数字, 其值必须在可接受的范围内
- @Past 被注释的元素必须是一个过去的日期
- @Future 被注释的元素必须是一个将来的日期
- @Pattern(value) 被注释的元素必须符合指定的正则表达式
- /**
- * @author Lang.Chen
- * @date 2018/6/20 下午 4:39
- */
- public class User {
- // 账号
- @NotBlank(message = "账号不能为空")
- private String phone;
- // 密码
- @NotBlank(message = "密码不能为空")
- private String password;
- public void setPhone(String phone) {
- this.phone = phone;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public String getPhone() {
- return phone;
- }
- public String getPassword() {
- return password;
- }
- }
2. 在 Controller 类里面对相应的接口添加 @Valid+BindingResult 验证, 加完以后, 如果参数验证不通过, 那就直接进入 if 语句里面, 在语句里面做相应的返回结果
- if(bindingResult.hasErrors()){
- }
- @RestController
- @RequestMapping(value = "/user")
- public class UserController implements IUser {
- @Override
- @RequestMapping(value = "/login", method = RequestMethod.POST)
- public User login( @Valid User userInfo, BindingResult bindingResult) {
- if (bindingResult.hasErrors()) {
- System.out.print(bindingResult.getFieldError().getDefaultMessage());
- return null;
- }
- return userInfo;
- }
- }
好了, 第一种验证我们已经说完了, 但你仔细想想, 如果我有多个接口, 是不是每次有要写 @Valid+BindgResult bingingResult, 然后再到 If 语句里面进行判断, 如果仅仅是错误信息不一致, 但返回的客户端结构是一致的, 比如
{"code":"1002","message":"parameters are missing","data":"{}"}
不同的验证只要改变 message, 那么是不是可以统一处理, 来减少代码量, 于是下面介绍第二种方式
使用 @Valid + 全局异常捕捉 参数校验
简单点说, 由第一种方式去掉 BindingResult, 然后再定义一个全局异常类, 同样的
1. 还是定义 POJO
- /**
- * @author Lang.Chen
- * @date 2018/6/20 下午 4:39
- */
- public class User {
- // 账号
- @NotBlank(message = "账号不能为空")
- private String phone;
- // 密码
- @NotBlank(message = "密码不能为空")
- private String password;
- public void setPhone(String phone) {
- this.phone = phone;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public String getPhone() {
- return phone;
- }
- public String getPassword() {
- return password;
- }
- }
2. 在 Controller 里面的方法定义要验证的 @Valid
- public class UserController implements IUser {
- @Override
- @RequestMapping(value = "/login2", method = RequestMethod.POST)
- public User login2(@RequestBody @Valid User userInfo) {
- return null;
- }
- }
3. 定义全局异常类
所有验证失败的结果都会在 GlobleExceptionHandler 的 defultExcepitonHandler 方法里面捕捉到
- @RestControllerAdvice
- @Component
- public class GlobleExceptionHandler {
- @ResponseBody
- @ExceptionHandler(Exception.class)
- public String defultExcepitonHandler(Exception ex) {
- ex.printStackTrace();
- if(ex instanceof BindException){
- // 处理返回的错误信息
- StringBuffer errorMsg = new StringBuffer();
- BindException c = (BindException) ex;
- List<ObjectError> errors = c.getBindingResult().getAllErrors();
- for (ObjectError error : errors) {
- errorMsg.append(error.getDefaultMessage()).append(";");
- }
- return errorMsg.toString();
- }
- return "";
- }
- }
有关于自定义验证和分组验证, 就不一一介绍了, 会使用以上 2 个就可以了.
Q&A
1. 如何使用单个参数验证, 并且进入全局异常捕捉, 返回?
有时候只传入少数验证时, 我们不可能都是新建一个对象来接受, 这时候如何使用单个参数验证呢, 看到网上使用在类上添加 @Validated 参数, 然后通过全局异常捕捉方式
- @RestController
- @RequestMapping("/order")
- @Validated
- public class OrderController implements IOrder {
- @RequestMapping(value = "/byId",method = RequestMethod.POST)
- public OrderInfo byId( @Max(value = 50) int orderId) {
- return null;
- }
- }
但每次访问接口时, 都是报同一个错误, 就没用这个方式了, 如果有人找到解决方案, 请指教, 谢谢
- {
- "timestamp": "2018-06-24T07:09:14.235+0000",
- "status": 404,
- "error": "Not Found",
- "message": "No message available",
- "path": "/order/byId"
- }
@Valid 和 @Validated 的区别
@Valid 是 javax.validation 里的 @Validated 是 @Valid 的一次封装, 是 Spring 提供的校验机制使用.@Validated 提供分组功能
来源: https://juejin.im/post/5b2f4514f265da597a6108f8