乐趣区

关于springboot:更简洁的参数校验使用-SpringBoot-Validation-对参数进行校验

在开发接口时,如果要对参数进行校验,你会怎么写?编写 if-else 吗?尽管也能达到成果,然而不够优雅。
明天,举荐一种更简洁的写法,应用 SpringBoot Validation 对办法参数进行校验,特地是在编写 Controller 层的办法时,间接应用一个注解即可实现参数校验。
示例代码:spring-validation-demo: SpringBootValidation Demo (gitee.com)
🚀引入依赖
想要实现上述所说的参数校验,咱们须要一个外围依赖:spring-boot-starter-validation,此外,为了不便演示,还须要其余依赖。
依赖如下:
 <dependencies>
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-validation</artifactId>
     </dependency>
     <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <optional>true</optional>
     </dependency>
   </dependencies>
复制代码
💡 以下局部不是核心内容:
你在编写上面的示例代码中,会发现次要应用到了 javax.validation.constraints 包下的注解,而这个包次要来自于 jakarta.validation-api 这个依赖。
如果引入依赖的时候间接引入 jakarta.validation-api 是无奈实现参数校验性能的,因为它只定义了标准,而没有具体实现。然而 hibernate-validator 实现了这个标准,间接引入 hibernate-validator 也是能够实现参数校验性能的。
 <!– 无奈实现性能 –>
 <dependency>
     <groupId>jakarta.validation</groupId>
     <artifactId>jakarta.validation-api</artifactId>
 </dependency>
 <!– 能够实现参数校验性能 –>
 <!–spring-boot-starter-validation 引入了这个依赖 –>
 <dependency>
     <groupId>org.hibernate.validator</groupId>
     <artifactId>hibernate-validator</artifactId>
 </dependency>
复制代码
🚀 相干注解阐明
这里列举出一些次要的注解,这些注解次要来自于包 javax.validation.constraints,有趣味查看源码的能够去这个包下查看。
能够先跳过这部分内容,上面的代码如果遇到不分明作用的注解再回来查阅。
✈ 空值查看

注解阐明 @NotBlank 用于字符串,字符串不能为 null 也不能为空字符串 @NotEmpty 字符串同上,对于汇合(Map,List,Set)不能为空,必须有元素 @NotNull 不能为 null@Null 必须为 null
✈ 数值查看

注解阐明 @DecimalMax(value)被正文的元素必须为数字,其值必须小于等于指定的值 @DecimalMin(value)被正文的元素必须为数字,其值必须大于等于指定的值 @Digits(integer, fraction)被正文的元素必须为数字,其值的整数局部精度为 integer,小数局部精度为 fraction@Positive 被正文的元素必须为负数 @PositiveOrZero 被正文的元素必须为负数或 0@Max(value)被正文的元素必须小于等于指定的值 @Min(value)被正文的元素必须大于等于指定的值 @Negative 被正文的元素必须为正数 @NegativeOrZero 被正文的元素必须为正数或 0
✈ Boolean 查看

注解阐明 @AssertFalse 被正文的元素必须值为 false@AssertTrue 被正文的元素必须值为 true
✈ 长度查看

注解阐明 @Size(min,max)被正文的元素长度必须在 min 和 max 之间,能够是 String、Collection、Map、数组
✈ 日期查看

注解阐明 @Future 被正文的元素必须是一个未来的日期 @FutureOrPresent 被正文的元素必须是当初或者未来的日期 @Past 被正文的元素必须是一个过来的日期 @PastOrPresent 被正文的元素必须是当初或者过来的日期
✈ 其余查看

注解阐明 @Email 被正文的元素必须是电子邮箱地址 @Pattern(regexp)被正文的元素必须合乎正则表达式

除此之外,org.hibernate.validator.constraints 包下还有其余校验注解,例如 @ISBN 查看一个字符串是否是一个无效地 ISBN 序列号。

🚀 参数校验
接下来开始体验 Spring Boot Validation。
首先,编写一个须要校验的实体类:
 @Data
 public class Student {
     @NotBlank(message = “ 主键不能为空 ”)
     private String id;
     @NotBlank(message = “ 名字不能为空 ”)
     @Size(min=2, max = 4, message = “ 名字字符长度必须为 2~4 个 ”)
     private String name;
     @Pattern(regexp = “^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\d|9[0-35-9])\d{8}$”, message = “ 手机号格局谬误 ”)
     private String phone;
     @Email(message = “ 邮箱格局谬误 ”)
     private String email;
     @Past(message = “ 生日必须早于以后工夫 ”)
     private Date birth;
     @Min(value = 0, message = “ 年龄必须为 0~100”)
     @Max(value = 100, message = “ 年龄必须为 0~100”)
     private Integer age;
     @PositiveOrZero
     private Double score;
 }
复制代码
随后编写一个管制层代码,进行测试:
 @RestController
 public class TestController {
 ​
     @GetMapping(“/test”)
     public Student test(@RequestBody @Validated Student student) {
         return student;
    }
 }
复制代码
应用 postman 进行测试,发送一个不带参数的申请,查看后果:

💡后端控制台日志打印是这样的(显示极度不敌对),能够看到校验规定失效了:
 2022-11-23 22:10:13.249 WARN 19840 — [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.example.springvalidationdemo.domain.Student com.example.springvalidationdemo.controller.TestController.test(com.example.springvalidationdemo.domain.Student) with 2 errors: [Field error in object ‘student’ on field ‘name’: rejected value [null]; codes [NotBlank.student.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.name,name]; arguments []; default message [name]]; default message [名字不能为空]] [Field error in object ‘student’ on field ‘id’: rejected value [null]; codes [NotBlank.student.id,NotBlank.id,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.id,id]; arguments []; default message [id]]; default message [主键不能为空]] ]
复制代码
🚀 全局异样解决
查看下面的日志打印,能够看到当参数校验不通过时,会抛出异样 MethodArgumentNotValidException,同时也会打印那些参数没有通过校验,以及该参数校验规定。
为了不便查看,咱们能够编写一个全局异样解决,解决这个参数校验异样,并应用对立返回实体返回给前端。
 @ControllerAdvice
 @Slf4j
 public class GlobalExceptionHandler {
 ​
     @ExceptionHandler(MethodArgumentNotValidException.class)
     @ResponseBody
     public ResponseEntity<Object> exception(MethodArgumentNotValidException e, HttpServletRequest request) {
         Map<String, String> result = new HashMap<>();
         BindingResult bindingResult = e.getBindingResult();
         log.error(“ 申请 [ {} ] {} 的参数校验产生谬误 ”, request.getMethod(), request.getRequestURL());
         for (ObjectError objectError : bindingResult.getAllErrors()) {
             FieldError fieldError = (FieldError) objectError;
             log.error(“ 参数 {} = {} 校验谬误:{}”, fieldError.getField(), fieldError.getRejectedValue(), fieldError.getDefaultMessage());
             result.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
         // 个别我的项目都会有本人定义的公共返回实体类,这里间接应用现成的 ResponseEntity 进行返回,同时设置 Http 状态码为 400
         return ResponseEntity.badRequest().body(result);
 ​
    }
 ​
 }
复制代码
再次应用 postman 发动测试:

控制台打印出自定义的日志信息:
 2022-11-23 22:16:37.800 ERROR 19880 — [nio-8080-exec-2] c.e.s.handler.GlobalExceptionHandler     : 申请 [GET] http://localhost:8080/test 的参数校验产生谬误
 2022-11-23 22:16:37.800 ERROR 19880 — [nio-8080-exec-2] c.e.s.handler.GlobalExceptionHandler     : 参数 name = null 校验谬误:名字不能为空
 2022-11-23 22:16:37.800 ERROR 19880 — [nio-8080-exec-2] c.e.s.handler.GlobalExceptionHandler     : 参数 id = null 校验谬误:主键不能为空
 2022-11-23 22:19:36.594 ERROR 19880 — [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 申请[GET] http://localhost:8080/test 的参数校验产生谬误
 2022-11-23 22:19:36.594 ERROR 19880 — [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 email = abc.com 校验谬误:邮箱格局谬误
 2022-11-23 22:19:36.594 ERROR 19880 — [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 score = -20 校验谬误:必须是负数或零
 2022-11-23 22:19:36.595 ERROR 19880 — [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 birth = Thu Jan 01 08:00:00 CST 2099 校验谬误:生日必须早于以后工夫
 2022-11-23 22:19:36.595 ERROR 19880 — [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 phone = 12233 校验谬误:手机号格局谬误
 2022-11-23 22:19:36.595 ERROR 19880 — [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 age = -40 校验谬误:年龄必须为 0~100
 2022-11-23 22:19:36.595 ERROR 19880 — [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 name = 我是很长的名字 校验谬误:名字字符长度必须为 2~4 个
 2022-11-23 22:19:36.595 ERROR 19880 — [nio-8080-exec-6] c.e.s.handler.GlobalExceptionHandler     : 参数 score = -20 校验谬误:须要在 0 和 9223372036854775807 之间
复制代码
🚀 传递校验
咱们也能够应用传递校验,即一个参数类中蕴含了另一个参数类,被蕴含的参数类也能够被校验。
在申明一个新的参数类,同时批改 Student 类。
 @Data
 public class ClassInfo {
     @NotBlank(message = “ 班主任姓名不能为空 ”)
     private String teacher;
     @NotNull(message = “ 老师不能为空 ”)
     private Integer classroom;
     @NotNull(message = “ 年级不能为空 ”)
     @Min(value = 1, message = “ 年级只能是 1-6”)
     @Max(value = 6, message = “ 年级只能是 1-6”)
     private Integer grade;
 }
 ​
 @Data
 public class Student {
    //………….
     // 新加的字段,被蕴含的参数类,应用 @Valid 就能传递校验,如果不应用 @Valid 注解,则无奈传递校验。
     @Valid
     private ClassInfo classInfo;
 }
复制代码
再应用 postman 测试一次

🚀 分组校验
此外还能够应用分组校验,令一组办法对某些字段校验,而令一组办法对其余字段校验,例如:个别状况下,新增实体的接口办法 [POST] 不须要主键 ID,批改实体的接口办法 [PUT] 就须要主键 ID 以便进行批改。
为注解 @Validated 赋值属性 value,以及为那些校验注解赋值属性 group,即可达到分组的成果。
接下来看看如何实现分组校验。
在 Student 类中增加两个外部接口 Inteface,同时批改 id 字段的注解,以进行分组
 @Data
 public class Student {
     // id 字段属于 Create 组
     @NotBlank(message = “ 主键不能为空 ”, groups = {Student.Create.class})
     private String id;
     // ………….
     // 更新分组
     public interface Update {}
 ​
     // 创立分组
     public interface Create {}
 }
复制代码
在管制层新增两个接口
 @RestController
 public class TestController {
 ​
     // @Validated 注解能够赋值 value 属性进行分组,value 是能够以数组的模式赋值,既能够调配多个组
     @PostMapping(“/students”)
     public Student create(@RequestBody @Validated(Student.Create.class) Student student) {
         return student;
    }
 ​
     @PutMapping(“/students”)
     public Student update(@RequestBody @Validated(Student.Update.class) Student student) {
         return student;
    }
 }
复制代码
在 postman 上进行测试:

能够看到分组校验也失效了。
🚀 总结
在理论开发中,咱们能够应用 Spring Boot Validation 提供的注解进行参数校验,进步代码的可读性,防止编写大量的 if-else 代码块和反复的校验语句。

退出移动版