Human0722's blog Human0722's blog
首页
  • Spring

    • Spring Framework
    • Spring Boot
    • Spring Cloud
  • CCNA
  • Vue

    • Vue2
日本语
导航
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Human0722

Gravity always win
首页
  • Spring

    • Spring Framework
    • Spring Boot
    • Spring Cloud
  • CCNA
  • Vue

    • Vue2
日本语
导航
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Spring Framework

  • Spring Boot

    • HelloWorld
    • 异常处理
    • 参数校验
      • 配置文件
      • Filter & Interceptor
      • Spring MVC 启动流程分析
      • DefineSprintBootStarter
    • Java 类库

    • 数据库

    • 解决方案

    • Java.Content
    • Spring Boot
    Xueliang
    2022-12-07
    目录

    参数校验

    通过注解来校验输入的数据。

    在 JSR-303 标准中定义了校验的接口, hibernate-validator 提供了实现。

    本文环境:

    • Spring Boot 2.7.6
    • Spring-boot-starter-validation: 2.7.6

    # 快速开始

    # 1、依赖引入

    <!-- validation -->
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
    </dependency>
    <!-- hibernate-validator-starter-for-spring-boot-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    # 2、属性添加校验

    class User {
    
      @NotNull
      private int id;
      @NotBlank(message = "不能为空")
      private String name;
      @NotEmpty
      private List<String>   
      //... getter setter constructor
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    在属性上加上注解 @NotNull 表示这个字段不能为空指针。 @NotBlank表示字符串至少含一个非空字符,@NotEmpty 表示集合至少包含一个非空元素。

    # 3、使用@Valid校验

    在控制器方法的参数前,加上 @Valid 表示要校验这个输入。当输入的数据都符合条件时,会直接开始执行控制器代码,如果参数校验错误, 则会抛出异常 MethodArgumentNotValidException, 走默认的流程响应异常,如何处理异常在下文。

    RestController
    public class ErrorController {
    
        @PostMapping("/users")
        public Response<String> addUsers(
                @Valid @RequestBody User user) {
            return Response.success("ok");
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    也可以通过在添加个形参 BindingResult 来接受参数校验的结果,此时即使参数校验不通过,也不会抛出异常,而是会将异常信息存储在 BindingResult中, 然后开始直接执行控制器的代码,如何处理 BindingResult 在下文中。

    # 4、常用注解

    # 校验失败处理

    校验失败处理通常是先获得错误的信息,然后在决定接下来的执行流程。通常是将错误信息封装成返回体返回。

    # 1、校验异常处理

    @RestControllerAdvice
    class GlobalExceptionHandler {
      
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public Map<String, String> handleValidationException(MethodArgumentNotValidException ex) {
            HashMap<String, String> errorMap = new HashMap<String, String>();
            ex.getBindingResult().getFieldErrors().forEach((item) -> {
                errorMap.put(item.getField(), item.getDefaultMessage());
            });
            return errorMap;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    # 2、BindingResult处理

    public class BindingResultUtil {
       static public void processBindingResult(BindingResult bindingResult) {
           if (bindingResult.hasErrors()) {
               StringBuilder stringBuilder = new StringBuilder();
               bindingResult.getFieldErrors().forEach((item)->{
                   stringBuilder.append(item.getField()).append(":").append(item.getDefaultMessage()).append(",");
               });
               // 自定义的 ServiceException
               throw new ServiceException(
                       ResponseCodeEnum.PARAMETER_ERROR.getCode(),
                       stringBuilder.toString());
           }
       }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    # 分组校验

    分组校验是在同一个类的基础上,通过分组的方式,产生不一样的属性校验组合。 最常见的场景就是新增和更新,可以公用一个类型来接受用户的输入,但是略有不同。比如新增的时候不需要数据的 id, 但是更新的时候需要。这个时候可以 定义两个类来分别接受,也可以公用同一个类,使用 分组校验 的方式来定义在某个分组下,属性的不同校验方式。

    # 1、 通过空接口来定义分组

    定义接口获得两个分组: AddGroup 和 UpdateGroup.

    public interface AddGroup{}
    public interface UpdateGroup{}
    
    1
    2

    # 2、校验注解加上分组

    public class User {
    
        @NotBlank(message = "id不能为空", groups = {UpdateGroup.class})
        private String id;
        @NotBlank(message = "name不能为空", groups = {AddGroup.class, UpdateGroup.class})
        private String name;
        @NotBlank(groups = {AddGroup.class})
        private String age;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    # 3、执行校验

    @Valid 并不支持分组校验,Spring 框架提供的 @Validated 支持分组校验。注解需要提供分组的信息来激活分组。

    @RestController
    public class ErrorController {
    
        @PostMapping("/users")
        public Response<String> addUsers(
                @Validated(AddGroup.class) @RequestBody User user) {
            return Response.success("ok");
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    # 自定义校验注解

    # 1、引入接口依赖

    自定义注解,即实现 JSR-303 接口,需要先引入接口依赖。

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
    </dependency>
    
    1
    2
    3
    4

    # 2、 编写自定义校验注解

    @Documented
    //指定校验器,可以指定多个校验器,会自动更具校验字段类型匹配不同的校验器
    @Constraint(validatedBy = {ListValueConstraintValidator.class})  
    @Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, TYPE_USE})
    public @interface ListValue {  
      // 必须字段1: 提示信息。 在类路径下定义 ValidationMessages.properties, key=value, key作为 default中的值
      String message() default "{xx.xxx.ListValue.message}";
      Class<?>[] groups() default { };
      Class<? extends Payload>[] payload() default { };
      int[] vals default { };
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    必要的三个字段: message, groups, payload.其余形式基本固定。

    # 3、 编写自定义的校验器

    class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
      @Override
      // 初始化方法
      public void initialize(ListValue constraintAnnotation) {
        int[] vals = constrainAnnotation.vals();
        for (int val : vals) {
          set.add(val);
        }
      }
      @Override
      public boolean isValid(Integer value, ConstraintValidatorContext context) {
        
        return set.contains(value);
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    implement ConstraintValidator<T,P>, T是注解类型, P是被注解的属性的类型。
    如果想这个注解支持多种数据类型,就要为每种类型定义一个校验器。然后在关联的时候传入这些校验器。

    # 4、 关联

    在第二步定义校验注解的位置,将校验器与校验注解关联,表示用此校验器来校验被注解的属性是否符合规则。

    @Constraint(validatedBy = {ListValueConstraintValidator.class})  
    
    1

    # 其他

    # 1、校验注解区别

    @Valid和@Validated都可以用来执行校验,但是 @Valid 不支持分组校验,@Validated支持分组校验。

    # 2、支持校验多种数据类型的注解

    定义多个校验器,然后在关联的时候,给注解传入多个校验器类即可。

    异常处理
    配置文件

    ← 异常处理 配置文件→

    最近更新
    01
    DefineSprintBootStarter
    03-23
    02
    Spring MVC 启动流程分析
    03-23
    03
    Redis
    03-23
    更多文章>
    Theme by Vdoing | Copyright © 2019-2024 Human0722 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式