原文地址:https://blog.csdn.net/qq_32352777/article/details/108424932

简述

@Validation 是一套帮助我们继续对传输的参数进行数据校验的注解,通过配置 Validation 可以很轻松的完成对数据的约束。

@Validated 作用在类、方法和参数上

  1. @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Validated {
  5. Class<?>[] value() default {};
  6. }

错误的状态码

返回的响应码推荐使用 400 bad request.

所有参数注解含义

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图1

项目依赖

Maven 依赖坐标:

说明:在演示项目中所有实体类均在包 entity,控制层均在包 controller。

全局异常处理类

说明:若不做异常处理,@Validated 注解的默认异常消息如下(示例):

  1. 2020-09-05 21:48:38.106 WARN 9796 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.example.validateddemo.controller.DemoController.validatedDemo1(com.example.validateddemo.entity.dto.UseDto): [Field error in object 'useDto' on field 'username': rejected value [null]; codes [NotBlank.useDto.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [useDto.username,username]; arguments []; default message [username]]; default message [用户名不能为空!]] ]

因此我们在这里做了一个全局的异常处理类,用于处理参数校验失败后抛出的异常,同时进行日志输出。

  1. package com.example.validateddemo.handler;
  2. import com.example.validateddemo.base.Result;
  3. import com.example.validateddemo.enums.ResultEnum;
  4. import com.example.validateddemo.utils.ResultUtil;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.http.HttpStatus;
  7. import org.springframework.validation.BindingResult;
  8. import org.springframework.validation.FieldError;
  9. import org.springframework.validation.ObjectError;
  10. import org.springframework.web.bind.MethodArgumentNotValidException;
  11. import org.springframework.web.bind.annotation.ControllerAdvice;
  12. import org.springframework.web.bind.annotation.ExceptionHandler;
  13. import org.springframework.web.bind.annotation.ResponseBody;
  14. import org.springframework.web.bind.annotation.ResponseStatus;
  15. import java.util.List;
  16. /**
  17. * @author He Changjie on 2020/9/5
  18. */
  19. @Slf4j
  20. @ControllerAdvice
  21. public class ValidatedExceptionHandler {
  22. /**
  23. * 处理@Validated参数校验失败异常
  24. * @param exception 异常类
  25. * @return 响应
  26. */
  27. @ResponseBody
  28. @ResponseStatus(HttpStatus.BAD_REQUEST)
  29. @ExceptionHandler(MethodArgumentNotValidException.class)
  30. public Result exceptionHandler(MethodArgumentNotValidException exception){
  31. BindingResult result = exception.getBindingResult();
  32. StringBuilder stringBuilder = new StringBuilder();
  33. if (result.hasErrors()) {
  34. List<ObjectError> errors = result.getAllErrors();
  35. if (errors != null) {
  36. errors.forEach(p -> {
  37. FieldError fieldError = (FieldError) p;
  38. log.warn("Bad Request Parameters: dto entity [{}],field [{}],message [{}]",fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage());
  39. stringBuilder.append(fieldError.getDefaultMessage());
  40. });
  41. }
  42. }
  43. return ResultUtil.validatedException(stringBuilder.toString());
  44. }
  45. }

基础参数校验

实体类

  1. package com.example.validateddemo.entity.dto;
  2. import lombok.Data;
  3. import org.springframework.format.annotation.DateTimeFormat;
  4. import javax.validation.constraints.*;
  5. /**
  6. * 用户实体
  7. * 数据传输对象
  8. * @author He Changjie on 2020/9/5
  9. */
  10. @Data
  11. public class User1Dto {
  12. /**
  13. * 用户名
  14. */
  15. @NotBlank(message = "用户名不能为空!")
  16. private String username;
  17. /**
  18. * 性别
  19. */
  20. @NotBlank(message = "性别不能为空!")
  21. private String gender;
  22. /**
  23. * 年龄
  24. */
  25. @Min(value = 1, message = "年龄有误!")
  26. @Max(value = 120, message = "年龄有误!")
  27. private int age;
  28. /**
  29. * 地址
  30. */
  31. @NotBlank(message = "地址不能为空!")
  32. private String address;
  33. /**
  34. * 邮箱
  35. */
  36. @Email(message = "邮箱有误!")
  37. private String email;
  38. /**
  39. * 手机号码
  40. */
  41. @Pattern(regexp = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$",message = "手机号码有误!")
  42. private String mobile;
  43. }

控制类

  1. package com.example.validateddemo.controller;
  2. import com.example.validateddemo.entity.dto.Use1Dto;
  3. import org.springframework.validation.annotation.Validated;
  4. import org.springframework.web.bind.annotation.PostMapping;
  5. import org.springframework.web.bind.annotation.RequestBody;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. /**
  9. * @author He Changjie on 2020/9/5
  10. */
  11. @RestController
  12. @RequestMapping("/api/v1")
  13. public class Demo1Controller {
  14. @PostMapping("/insert")
  15. public String validatedDemo1(@Validated @RequestBody Use1Dto use1Dto){
  16. System.out.println(use1Dto);
  17. return "success";
  18. }
  19. }

测试

1、参数校验通过:

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图2

2、参数校验不通过:

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图3

嵌套参数验证

验证实体中的其他需要被验证的对象集合或其他对象

实体类

  1. package com.example.validateddemo.entity.dto;
  2. import lombok.Data;
  3. import javax.validation.Valid;
  4. import javax.validation.constraints.NotBlank;
  5. import javax.validation.constraints.NotNull;
  6. import java.util.List;
  7. /**
  8. * 队伍实体
  9. * 数据传输对象
  10. * @author He Changjie on 2020/9/5
  11. */
  12. @Data
  13. public class Team1Dto {
  14. /**
  15. * 队伍名称
  16. */
  17. @NotBlank(message = "队伍名称不能为空!")
  18. private String name;
  19. /**
  20. * 队伍人员
  21. */
  22. @NotNull(message = "队伍人员不能为空!")
  23. @Valid
  24. private List<User1Dto> userList;
  25. /**
  26. * 队伍负责人
  27. */
  28. @NotNull(message = "队伍负责人不能为空!")
  29. @Valid
  30. private User1Dto user;
  31. }

控制类

  1. package com.example.validateddemo.controller;
  2. import com.example.validateddemo.base.Result;
  3. import com.example.validateddemo.entity.dto.Team1Dto;
  4. import com.example.validateddemo.entity.dto.Use1Dto;
  5. import com.example.validateddemo.utils.ResultUtil;
  6. import org.springframework.validation.annotation.Validated;
  7. import org.springframework.web.bind.annotation.PostMapping;
  8. import org.springframework.web.bind.annotation.RequestBody;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.bind.annotation.RestController;
  11. /**
  12. * @author He Changjie on 2020/9/5
  13. */
  14. @RestController
  15. @RequestMapping("/api/v1")
  16. public class Demo1Controller {
  17. @PostMapping("/insert")
  18. public Result validatedDemo1(@Validated @RequestBody Use1Dto use1Dto){
  19. return ResultUtil.success(use1Dto);
  20. }
  21. @PostMapping("/insert2")
  22. public Result validatedDemo2(@Validated @RequestBody Team1Dto team1Dto){
  23. return ResultUtil.success(team1Dto);
  24. }
  25. }

测试

1、参数验证通过:

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图4

2、参数验证不通过:

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图5

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图6

分组参数验证

将不同的校验规则分给不同的组,在使用时,指定不同的校验规则

接口类

  1. package com.example.validateddemo.interfaces;
  2. /**
  3. * 校验分组1
  4. * @author He Changjie on 2020/9/5
  5. */
  6. public interface Group1 {
  7. }
  1. package com.example.validateddemo.interfaces;
  2. /**
  3. * 校验分组2
  4. * @author He Changjie on 2020/9/5
  5. */
  6. public interface Group2 {
  7. }

实体类

  1. package com.example.validateddemo.entity.dto;
  2. import com.example.validateddemo.interfaces.Group1;
  3. import com.example.validateddemo.interfaces.Group2;
  4. import lombok.Data;
  5. import javax.validation.constraints.*;
  6. /**
  7. * @author He Changjie on 2020/9/5
  8. */
  9. @Data
  10. public class User2Dto {
  11. /**
  12. * 用户名
  13. */
  14. @NotBlank(message = "用户名不能为空!", groups = {Group1.class})
  15. private String username;
  16. /**
  17. * 性别
  18. */
  19. @NotBlank(message = "性别不能为空!")
  20. private String gender;
  21. /**
  22. * 年龄
  23. */
  24. @Min(value = 1, message = "年龄有误!", groups = {Group1.class})
  25. @Max(value = 120, message = "年龄有误!", groups = {Group2.class})
  26. private int age;
  27. /**
  28. * 地址
  29. */
  30. @NotBlank(message = "地址不能为空!")
  31. private String address;
  32. /**
  33. * 邮箱
  34. */
  35. @Email(message = "邮箱有误!", groups = {Group2.class})
  36. private String email;
  37. /**
  38. * 手机号码
  39. */
  40. @Pattern(regexp = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$",message = "手机号码有误!", groups = {Group2.class})
  41. private String mobile;
  42. }

控制类

  1. package com.example.validateddemo.controller;
  2. import com.example.validateddemo.base.Result;
  3. import com.example.validateddemo.entity.dto.Team1Dto;
  4. import com.example.validateddemo.entity.dto.User1Dto;
  5. import com.example.validateddemo.entity.dto.User2Dto;
  6. import com.example.validateddemo.interfaces.Group1;
  7. import com.example.validateddemo.interfaces.Group2;
  8. import com.example.validateddemo.utils.ResultUtil;
  9. import org.springframework.validation.annotation.Validated;
  10. import org.springframework.web.bind.annotation.PostMapping;
  11. import org.springframework.web.bind.annotation.RequestBody;
  12. import org.springframework.web.bind.annotation.RequestMapping;
  13. import org.springframework.web.bind.annotation.RestController;
  14. /**
  15. * @author He Changjie on 2020/9/5
  16. */
  17. @RestController
  18. @RequestMapping("/api/v1")
  19. public class Demo1Controller {
  20. @PostMapping("/insert")
  21. public Result validatedDemo1(@Validated @RequestBody User1Dto user1Dto){
  22. return ResultUtil.success(user1Dto);
  23. }
  24. @PostMapping("/insert2")
  25. public Result validatedDemo2(@Validated @RequestBody Team1Dto team1Dto){
  26. return ResultUtil.success(team1Dto);
  27. }
  28. @PostMapping("/insert3")
  29. public Result validatedDemo3(@Validated @RequestBody User2Dto user2Dto){
  30. return ResultUtil.success(user2Dto);
  31. }
  32. @PostMapping("/insert4")
  33. public Result validatedDemo4(@Validated(Group1.class) @RequestBody User2Dto user2Dto){
  34. return ResultUtil.success(user2Dto);
  35. }
  36. @PostMapping("/insert5")
  37. public Result validatedDemo5(@Validated(Group2.class) @RequestBody User2Dto user2Dto){
  38. return ResultUtil.success(user2Dto);
  39. }
  40. }

测试

1、未分组校验通过:

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图7

2、未分组参数校验不通过:

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图8
3、分组 1 参数校验通过

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图9

4、分组 1 参数校验不通过

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图10

5、分组 2 参数校验通过

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图11

6、分组 2 参数校验不通过

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图12

7、使用默认分组,参数校验通过:

说明:将控制层 / insert3 接口调整如下后测试

  1. @PostMapping("/insert3")
  2. public Result validatedDemo3(@Validated(Default.class) @RequestBody User2Dto user2Dto){
  3. return ResultUtil.success(user2Dto);
  4. }

Default.class 为 Validated 依赖中含有的接口类,非自定义接口类

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图13

8、使用默认分组,参数校验不通过:

说明:同第 7 点相同操作

@Validated注解详解,分组校验,嵌套校验,@Valid和@Validated 区别,Spring Boot @Validated - 图14

@Valid 和 @Validated 区别

通过源码分析:

  1. @Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Valid {
  5. }
  1. @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Validated {
  5. Class<?>[] value() default {};
  6. }

@Valid:没有分组的功能。

@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上

@Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制

@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上

两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能

码云地址:https://gitee.com/jie_harris/validateddemo.git