• Spring Boot @Validated 和Javax的@Valid配合使用


    一、@Validated 和@Valid有什么用

    @Validation 和@Valid 常常配合使用对传输的参数进行数据校验的注解,并通过配置全局异常处理器进行合理化的提示,增加用户的体验

    并且@Validated可以通过分组来指定什么时候触发什么样的参数校验(这里看一下就行,下面有说什么是分组)

    二、为什么要使用@Validated 和@Valid的思考?

    其实不用这两个注解也可以完成对传输的参数进校验,那样我们就需要一直写if语句进行判断 ,如果不为xxx,抛出异常,然后进行捕获处理  

    但是当多处都用到的一样的传输参数的时候,我们每次都需要写一些重复的if进行校验,其实代码是不优雅的。因此有了这两个组件来帮我们进行传输参数的校验。

    三、使用方法

    3.1 引入pom

    1. org.springframework.boot
    2. spring-boot-starter-web
    3. org.springframework.boot
    4. spring-boot-starter-validation
    5. org.projectlombok
    6. lombok
    7. org.springframework.boot
    8. spring-boot-starter-test
    9. test
    10. junit
    11. junit

    3.2 写实体类加入一写校验注解

    常用的校验注解

         

    1. package com.sofwin.validator.domain;
    2. import com.sofwin.validator.config.InsertGroup;
    3. import com.sofwin.validator.config.Status;
    4. import com.sofwin.validator.config.UpdateGroup;
    5. import lombok.Data;
    6. import org.hibernate.validator.constraints.Range;
    7. import org.springframework.web.bind.annotation.Mapping;
    8. import javax.validation.Valid;
    9. import javax.validation.constraints.NotBlank;
    10. import javax.validation.constraints.NotNull;
    11. import javax.validation.constraints.Size;
    12. import java.math.BigDecimal;
    13. import java.util.List;
    14. /**
    15. * @packageName: com.sofwin.validator.domain
    16. * @author: wentao
    17. * @date: 2023/9/4 21:17
    18. * @version: 1.0
    19. * @email 1660420659@qq.com
    20. * @description: 测试
    21. */
    22. @Data
    23. public class UserR {
    24. @NotBlank(message = "名称不能为空")
    25. private String name;
    26. @NotNull(message = "年龄不能为空")
    27. @Range(min = 1,max = 200,message ="最小为{min}岁,最大为{max}岁" )
    28. private Integer age;
    29. @Size(message ="编号长度为 [4-8] ", min = 4, max = 8)
    30. private String idNo;
    31. }

    3.3 编写controller层进行测试 (请求参数是对象)

    注意需要在请求参数的前面加上@Valid注解

    统一返回类

    1. package com.sofwin.validator.config;
    2. import lombok.Data;
    3. /**
    4. * @packageName: com.sofwin.validator.config
    5. * @author: wentao
    6. * @date: 2023/9/4 21:34
    7. * @version: 1.0
    8. * @email 1660420659@qq.com
    9. * @description: 统一返回参数
    10. */
    11. @Data
    12. public class BaseResult {
    13. private int code;
    14. private String message;
    15. private T data;
    16. public BaseResult() {
    17. }
    18. public BaseResult(int code, T data,String message) {
    19. this.code = code;
    20. this.message = message;
    21. this.data = data;
    22. }
    23. public static BaseResult build(int code, T data, String message) {
    24. return new BaseResult(code,data,message);
    25. }
    26. public static BaseResult build( T data, BaseResult resultCodeEnum) {
    27. return new BaseResult(resultCodeEnum.getCode(),data,resultCodeEnum.getMessage());
    28. }
    29. public static BaseResult ok(T data) {
    30. return new BaseResult<>(20000,data,"success");
    31. }
    32. public static BaseResult fail(T data) {
    33. return new BaseResult<>(50000,data,"error");
    34. }
    35. }

    controller 

    1. package com.sofwin.validator.controller;
    2. import com.sofwin.validator.config.BaseResult;
    3. import com.sofwin.validator.config.InsertGroup;
    4. import com.sofwin.validator.domain.UserR;
    5. import org.springframework.validation.annotation.Validated;
    6. import org.springframework.web.bind.annotation.*;
    7. import javax.validation.Valid;
    8. import javax.validation.constraints.NotNull;
    9. /**
    10. * @packageName: com.sofwin.validator.controller
    11. * @author: wentao
    12. * @date: 2023/9/4 21:14
    13. * @version: 1.0
    14. * @description: 请求参数校验
    15. */
    16. @RestController
    17. @RequestMapping("validator")
    18. public class ValidatorController {
    19. @PostMapping("/validPost")
    20. public BaseResult validPostTest(@Valid @RequestBody UserR user ) {
    21. return BaseResult.ok(user);
    22. }
    23. }

    正常参数

     

    非正常参数 

    我们发现没有提示我们在实体中message中写的提示信息,是因为我们没有设置全局异常处理

    ,它只是在控制台返回了提示信息 

    2023-09-05 21:24:03.305  WARN 20924 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.sofwin.validator.config.BaseResult com.sofwin.validator.controller.ValidatorController.validPostTest(com.sofwin.validator.domain.UserR): [Field error in object 'userR' on field 'age': rejected value [1111]; codes [Range.userR.age,Range.age,Range.java.lang.Integer,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userR.age,age]; arguments []; default message [age],200,1]; default message [最小为1岁,最大为200岁]] ]

    全局异常处理器

    1. package com.sofwin.validator.config;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.slf4j.LoggerFactory;
    4. import org.springframework.validation.BindException;
    5. import org.springframework.web.bind.annotation.ControllerAdvice;
    6. import org.springframework.web.bind.annotation.ExceptionHandler;
    7. import org.springframework.web.bind.annotation.RestControllerAdvice;
    8. import javax.validation.ConstraintViolation;
    9. import javax.validation.ConstraintViolationException;
    10. import java.util.Set;
    11. import org.slf4j.Logger;
    12. /**
    13. * @packageName: com.sofwin.validator.config
    14. * @author: wentao
    15. * @date: 2023/9/4 21:28
    16. * @version: 1.0
    17. * @email 1660420659@qq.com
    18. * @description: 全局异常处理器
    19. */
    20. @RestControllerAdvice
    21. @Slf4j
    22. public class GlobExceptionHandeler {
    23. private final Logger logger = LoggerFactory.getLogger(getClass());
    24. //valid参数校验出现异常
    25. @ExceptionHandler(BindException.class)
    26. public BaseResult bindException(BindException e) {
    27. logger.error("valid参数校验出现异常");
    28. System.out.println(e);
    29. return BaseResult.build(441,null,e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
    30. }
    31. //validated参数校验出现异常
    32. @ExceptionHandler(ConstraintViolationException.class)
    33. public BaseResult constraintViolationException(ConstraintViolationException e) {
    34. logger.error("validated参数校验出现异常");
    35. return BaseResult.build(441,null,e.getLocalizedMessage().split(":")[1].trim());
    36. }
    37. @ExceptionHandler(Exception.class)
    38. public BaseResult constraintViolationException(Exception e) {
    39. logger.error("Exception");
    40. return BaseResult.build(441,null,e.getLocalizedMessage());
    41. }
    42. }

     加入全局异常处理器后非正常参数返回结果:

    3.4 当参数不是一个对象

     一定要在controller上加入@Validated才生效

    1. @RestController
    2. @RequestMapping("validator")
    3. @Validated
    4. public class ValidatorController {
    5. @PostMapping("/validPost")
    6. public BaseResult validPostTest(@Valid @RequestBody UserR user ) {
    7. return BaseResult.ok(user);
    8. }
    9. @GetMapping("/validGet1")
    10. public BaseResult validGetTest( @NotBlank(message = "名字不能为空") String name ) {
    11. return BaseResult.ok(name);
    12. }
    13. }

     

    3.5 分组

    当我们在特定情况下才进行参数校验才进行分组,例如只有当我们插入的时候我们进行校验,其他时候不进行参数的校验,这个时候就可以使用分组

    实体类

    1. package com.sofwin.validator.domain;
    2. import com.sofwin.validator.config.InsertGroup;
    3. import com.sofwin.validator.config.Status;
    4. import com.sofwin.validator.config.UpdateGroup;
    5. import lombok.Data;
    6. import org.hibernate.validator.constraints.Range;
    7. import org.springframework.web.bind.annotation.Mapping;
    8. import javax.validation.Valid;
    9. import javax.validation.constraints.NotBlank;
    10. import javax.validation.constraints.NotNull;
    11. import javax.validation.constraints.Size;
    12. import java.math.BigDecimal;
    13. import java.util.List;
    14. /**
    15. * @packageName: com.sofwin.validator.domain
    16. * @author: wentao
    17. * @date: 2023/9/4 21:17
    18. * @version: 1.0
    19. * @email 1660420659@qq.com
    20. * @description: 测试
    21. */
    22. @Data
    23. public class UserR {
    24. @NotBlank(message = "名称不能为空")
    25. //进行分组
    26. @NotBlank(message = "名称不能为空(InsertGroup)",groups = InsertGroup.class)
    27. private String name;
    28. @NotNull(message = "年龄不能为空")
    29. @NotNull(message = "年龄不能为空(InsertGroup)",groups = {InsertGroup.class,UpdateGroup.class})
    30. @Range(min = 1,max = 200,message ="最小为{min}岁,最大为{max}岁" )
    31. private Integer age;
    32. @Size(message ="编号长度为 [4-8] ", min = 4, max = 8)
    33. private String idNo;
    34. }

    其中InsertGroup和UpdateGroup只是一个普通的接口

    1. public interface InsertGroup {
    2. }
    3. public interface UpdateGroup {
    4. }

     

    controller

    1. package com.sofwin.validator.controller;
    2. import com.sofwin.validator.config.BaseResult;
    3. import com.sofwin.validator.config.InsertGroup;
    4. import com.sofwin.validator.domain.UserR;
    5. import org.springframework.validation.annotation.Validated;
    6. import org.springframework.web.bind.annotation.*;
    7. import javax.validation.Valid;
    8. import javax.validation.constraints.NotBlank;
    9. import javax.validation.constraints.NotNull;
    10. /**
    11. * @packageName: com.sofwin.validator.controller
    12. * @author: wentao
    13. * @date: 2023/9/4 21:14
    14. * @version: 1.0
    15. * @description: 请求参数校验
    16. */
    17. @RestController
    18. @RequestMapping("validator")
    19. @Validated
    20. public class ValidatorController {
    21. @PostMapping("/validPost")
    22. public BaseResult validPostTest(@Valid @RequestBody UserR user ) {
    23. return BaseResult.ok(user);
    24. }
    25. @PostMapping("/validPost2")
    26. public BaseResult validPostTest2(@Validated(InsertGroup.class) @RequestBody UserR user ) {
    27. return BaseResult.ok(user);
    28. }
    29. @GetMapping("/validGet1")
    30. public BaseResult validGetTest( @NotBlank(message = "名字不能为空") String name ) {
    31. return BaseResult.ok(name);
    32. }
    33. @GetMapping("/validGet2")
    34. public BaseResult validGetTest2(@Validated(InsertGroup.class) @NotNull(message = "名字不能为空") String name ) {
    35. return BaseResult.ok(name);
    36. }
    37. }

    测试

    validPost2、validGet2
    validPost2 

    validGet2

    3.6 自定义注解

    当它提供的注解我们没有办法解决我们的问题的时候,我们就可以自定义注解

    定义注解Status

    1. package com.sofwin.validator.config;
    2. import javax.validation.Constraint;
    3. import javax.validation.Payload;
    4. import java.lang.annotation.*;
    5. /**
    6. * @packageName: com.sofwin.validator.config
    7. * @author: wentao
    8. * @date: 2023/9/4 21:52
    9. * @version: 1.0
    10. * @email 1660420659@qq.com
    11. * @description: 自定义校验注解
    12. */
    13. @Target({ElementType.FIELD,ElementType.PARAMETER})
    14. @Retention(RetentionPolicy.RUNTIME)
    15. @Documented
    16. //自己写校验规则
    17. @Constraint(validatedBy = {StatusConstraint.class})
    18. //元注解 可以分组使用,如果不写定义相同的注解会出现错误
    19. @Repeatable(Status.List.class)
    20. public @interface Status {
    21. String[] statusType() default {};
    22. String message() default "状态传递有误";
    23. Class[] groups() default {};
    24. Classextends Payload>[] payload() default {};
    25. @Target({ElementType.FIELD,ElementType.PARAMETER})
    26. @Retention(RetentionPolicy.RUNTIME)
    27. @Documented
    28. @interface List {
    29. Status[] value();
    30. }
    31. }

    校验规则

    1. package com.sofwin.validator.config;
    2. import javax.validation.ConstraintValidator;
    3. import javax.validation.ConstraintValidatorContext;
    4. import java.lang.annotation.Annotation;
    5. import java.util.Arrays;
    6. import java.util.List;
    7. /**
    8. * @packageName: com.sofwin.validator.config
    9. * @author: wentao
    10. * @date: 2023/9/4 22:04
    11. * @version: 1.0
    12. * @email 1660420659@qq.com
    13. * @description: 自定义校验柜子
    14. */
    15. public class StatusConstraint implements ConstraintValidator {
    16. List statusType;
    17. /**
    18. * 一般进行初始化
    19. * @param constraintAnnotation
    20. */
    21. @Override
    22. public void initialize(Status constraintAnnotation) {
    23. String[] strings = constraintAnnotation.statusType();
    24. statusType = Arrays.asList(strings);
    25. }
    26. /**
    27. *
    28. * @param value 参数的值
    29. * @param context
    30. * @return true 通过不抛出异常 fasle不通过抛出异常
    31. */
    32. @Override
    33. public boolean isValid(Integer value, ConstraintValidatorContext context) {
    34. if (value !=null) {
    35. if (!statusType.contains(String.valueOf(value))) {
    36. return false;
    37. }
    38. return true;
    39. }
    40. return false;
    41. }
    42. }

    测试

    实体类

    1. package com.sofwin.validator.domain;
    2. import com.sofwin.validator.config.InsertGroup;
    3. import com.sofwin.validator.config.Status;
    4. import com.sofwin.validator.config.UpdateGroup;
    5. import lombok.Data;
    6. import org.hibernate.validator.constraints.Range;
    7. import org.springframework.web.bind.annotation.Mapping;
    8. import javax.validation.Valid;
    9. import javax.validation.constraints.NotBlank;
    10. import javax.validation.constraints.NotNull;
    11. import javax.validation.constraints.Size;
    12. import java.math.BigDecimal;
    13. import java.util.List;
    14. /**
    15. * @packageName: com.sofwin.validator.domain
    16. * @author: wentao
    17. * @date: 2023/9/4 21:17
    18. * @version: 1.0
    19. * @email 1660420659@qq.com
    20. * @description: 测试
    21. */
    22. @Data
    23. public class UserR {
    24. @NotBlank(message = "名称不能为空")
    25. //进行分组
    26. @NotBlank(message = "名称不能为空(InsertGroup)",groups = InsertGroup.class)
    27. private String name;
    28. @NotNull(message = "年龄不能为空")
    29. @NotNull(message = "年龄不能为空(InsertGroup)",groups = {InsertGroup.class,UpdateGroup.class})
    30. @Range(min = 1,max = 200,message ="最小为{min}岁,最大为{max}岁" )
    31. private Integer age;
    32. @Status(statusType = {"1","2"})
    33. private Integer status;
    34. @Size(message ="编号长度为 [4-8] ", min = 4, max = 8)
    35. private String idNo;
    36. }

    controller 

    1. @PostMapping("/validPost")
    2. public BaseResult validPostTest(@Valid @RequestBody UserR user ) {
    3. return BaseResult.ok(user);
    4. }

    正常传参

    非正常传参

     3.7 当出现类中出现嵌套的情况

    当出现嵌套的情况只需要在类的属性中在加一个注解@Valid

    实体类

    1. package com.sofwin.validator.domain;
    2. import com.sofwin.validator.config.InsertGroup;
    3. import com.sofwin.validator.config.Status;
    4. import com.sofwin.validator.config.UpdateGroup;
    5. import lombok.Data;
    6. import org.hibernate.validator.constraints.Range;
    7. import org.springframework.web.bind.annotation.Mapping;
    8. import javax.validation.Valid;
    9. import javax.validation.constraints.NotBlank;
    10. import javax.validation.constraints.NotNull;
    11. import javax.validation.constraints.Size;
    12. import java.math.BigDecimal;
    13. import java.util.List;
    14. /**
    15. * @packageName: com.sofwin.validator.domain
    16. * @author: wentao
    17. * @date: 2023/9/4 21:17
    18. * @version: 1.0
    19. * @email 1660420659@qq.com
    20. * @description: 测试
    21. */
    22. @Data
    23. public class UserR {
    24. @NotBlank(message = "名称不能为空")
    25. //进行分组
    26. @NotBlank(message = "名称不能为空(InsertGroup)",groups = InsertGroup.class)
    27. private String name;
    28. @NotNull(message = "年龄不能为空")
    29. @NotNull(message = "年龄不能为空(InsertGroup)",groups = {InsertGroup.class,UpdateGroup.class})
    30. @Range(min = 1,max = 200,message ="最小为{min}岁,最大为{max}岁" )
    31. private Integer age;
    32. @Status(statusType = {"1","2"})
    33. private Integer status;
    34. @Size(message ="编号长度为 [4-8] ", min = 4, max = 8)
    35. private String idNo;
    36. //嵌套使用valid才能生效
    37. @Valid
    38. private List sonUserList;
    39. }
    1. package com.sofwin.validator.domain;
    2. import com.sofwin.validator.config.InsertGroup;
    3. import com.sofwin.validator.config.UpdateGroup;
    4. import lombok.Data;
    5. import javax.validation.constraints.NotBlank;
    6. import javax.validation.constraints.NotNull;
    7. import javax.validation.constraints.Size;
    8. import java.math.BigDecimal;
    9. /**
    10. * @packageName: com.sofwin.validator.domain
    11. * @author: wentao
    12. * @date: 2023/9/4 21:17
    13. * @version: 1.0
    14. * @email 1660420659@qq.com
    15. * @description: 测试
    16. */
    17. @Data
    18. public class SonUser {
    19. @NotBlank(message = "sonName不能为空")
    20. private String sonName;
    21. }

    controller

    1.   @PostMapping("/validPost")
    2.     public BaseResult validPostTest(@Valid @RequestBody UserR user ) {
    3.        return BaseResult.ok(user);
    4.     }

    四、 总结 

    @Valid:没有分组的功能。

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

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

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

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

  • 相关阅读:
    Jmeter工具下载并直连MySQL数据库
    MYSQL:主从复制简述
    正厚干货 | 软件测试面试题库
    C# 方法参数out(实现int.Tyrparse()方法)
    探索实人认证API:保障在线交互安全的关键一步
    网络——多区域OSPF配置(OSPF系列第1篇)
    计算机组成原理 | 总线
    Unity DOTS系列之Aspect核心机制分析
    【Linux内核代码分析1】Linux时间子系统及HRTIMER实现
    网络安全(黑客技术)自学规划
  • 原文地址:https://blog.csdn.net/weixin_52574640/article/details/132679120