【Spring 校验】
作者:mmseoamin日期:2023-12-14

校验

  • 🍇 概述
    • 🍉 使用场景
    • 🍉 依赖引入
    • 🍍 校验示例
      • 🍈(1)在实体上标记校验注解
      • 🍒(2)在方法参数上声明校验注解
      • 🍏(3)抛异常
      • 🥝 异常统一处理

        【Spring 校验】,在这里插入图片描述,第1张

        主页传送门:📀 传送

        🍇 概述


          Java API 规范(JSR303)定义了Bean校验的标准validation-api,但没有提供具体的实现方法。hibernate validation是对这个规范的实现,并增加了如@Email、@Length等校验注解。Spring Validation是对hibernate validation的二次封装,用于支持spring mvc参数的自动校验。

        🍉 使用场景

        Spring 校验使用场景

        • Spring 常规校验(Validator)
        • Spring 数据绑定(DataBinder)
        • Spring Web 参数绑定(WebDataBinder)
        • Spring WebMVC/WebFlux 处理方法参数校验

          🍉 依赖引入


            如果 spring-boot 版本小于 2.3.x,spring-boot-starter-web 会自动传入 hibernate-validator 依赖。如果 spring-boot 版本大于 2.3.x,则需要手动引入依赖:

          
            org.hibernate.validator
            hibernate-validator-parent
            6.2.5.Final
          
          

            对于 web 服务来说,为防止非法参数对业务造成影响,在 Controller 层一定要做参数校验的!大部分情况下,请求参数分为如下两种形式:

          • POST、PUT 请求,使用 requestBody 传递参数;
          • GET 请求,使用 requestParam/PathVariable 传递参数。

              实际上,不管是 requestBody 参数校验还是方法级别的校验,最终都是调用 Hibernate Validator 执行校验,Spring Validation 只是做了一层封装。

            🍍 校验示例


            🍈(1)在实体上标记校验注解

            示例

            @Data
            @NoArgsConstructor
            @AllArgsConstructor
            public class User implements Serializable {
                @NotNull
                private Long id;
                @NotBlank
                @Size(min = 2, max = 10)
                private String name;
                @Min(value = 1)
                @Max(value = 100)
                private Integer age;
            }
            

              上述类使用了Lombok库的@Data、@NoArgsConstructor和@AllArgsConstructor注解,分别用于自动生成getter、setter、无参构造方法和全参构造方法

            🍒(2)在方法参数上声明校验注解

            @Slf4j
            @Validated
            @RestController
            @RequestMapping("validate1")
            public class ValidatorController {
                /**
                 * {@link RequestBody} 参数校验
                 */
                @PostMapping(value = "save")
                public DataResult save(@Valid @RequestBody User entity) {
                    log.info("保存一条记录:{}", JSONUtil.toJsonStr(entity));
                    return DataResult.ok(true);
                }
                /**
                 * {@link RequestParam} 参数校验
                 */
                @GetMapping(value = "queryByName")
                public DataResult queryByName(
                    @RequestParam("username")
                    @NotBlank
                    @Size(min = 2, max = 10)
                    String name
                ) {
                    User user = new User(1L, name, 18);
                    return DataResult.ok(user);
                }
                /**
                 * {@link PathVariable} 参数校验
                 */
                @GetMapping(value = "detail/{id}")
                public DataResult detail(@PathVariable("id") @Min(1L) Long id) {
                    User user = new User(id, "李四", 18);
                    return DataResult.ok(user);
                }
            }
            

              上述类ValidatorController,使用了Spring框架。它是一个RESTful风格的控制器,用于处理HTTP请求。这个类有三个方法:

            • save方法:使用@PostMapping注解,处理POST请求,路径为"validate1/save"。该方法接收一个User类型的参数entity,使用@Valid和@RequestBody注解进行参数校验。如果校验通过,将记录保存到数据库,并返回一个表示成功的DataResult对象。

            • queryByName方法:使用@GetMapping注解,处理GET请求,路径为"validate1/queryByName"。该方法接收一个名为username的请求参数,使用@NotBlank、@Size注解进行参数校验。如果校验通过,根据name查询用户信息,并返回一个包含用户信息的DataResult对象。

            • detail方法:使用@GetMapping注解,处理GET请求,路径为"validate1/detail/{id}"。该方法接收一个名为id的路径变量,使用@Min注解进行参数校验。如果校验通过,根据id查询用户信息,并返回一个包含用户信息的DataResult对象。

              🍏(3)抛异常

                如果请求参数不满足校验规则,则会抛出ConstraintViolationException或MethodArgumentNotValidException异常。

              🥝 异常统一处理

                在实际项目开发中,通常会用统一异常处理来返回一个更友好的提示。

              @Slf4j
              @ControllerAdvice
              public class GlobalExceptionHandler {
                  /**
                   * 处理所有不可知的异常
                   */
                  @ResponseBody
                  @ResponseStatus(HttpStatus.OK)
                  @ExceptionHandler(Throwable.class)
                  public Result handleException(Throwable e) {
                      log.error("未知异常", e);
                      return new Result(ResultStatus.HTTP_SERVER_ERROR.getCode(), e.getMessage());
                  }
                  /**
                   * 统一处理请求参数校验异常(普通传参)
                   *
                   * @param e ConstraintViolationException
                   * @return {@link DataResult}
                   */
                  @ResponseBody
                  @ResponseStatus(HttpStatus.BAD_REQUEST)
                  @ExceptionHandler({ ConstraintViolationException.class })
                  public Result handleConstraintViolationException(final ConstraintViolationException e) {
                      log.error("ConstraintViolationException", e);
                      List errors = new ArrayList<>();
                      for (ConstraintViolation violation : e.getConstraintViolations()) {
                          Path path = violation.getPropertyPath();
                          List pathArr = StrUtil.split(path.toString(), ',');
                          errors.add(pathArr.get(0) + " " + violation.getMessage());
                      }
                      return new Result(ResultStatus.REQUEST_ERROR.getCode(), CollectionUtil.join(errors, ","));
                  }
                  /**
                   * 处理参数校验异常
                   *
                   * @param e MethodArgumentNotValidException
                   * @return {@link DataResult}
                   */
                  @ResponseBody
                  @ResponseStatus(HttpStatus.BAD_REQUEST)
                  @ExceptionHandler({ MethodArgumentNotValidException.class })
                  private Result handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) {
                      log.error("MethodArgumentNotValidException", e);
                      List errors = new ArrayList<>();
                      for (ObjectError error : e.getBindingResult().getAllErrors()) {
                          errors.add(((FieldError) error).getField() + " " + error.getDefaultMessage());
                      }
                      return new Result(ResultStatus.REQUEST_ERROR.getCode(), CollectionUtil.join(errors, ","));
                  }
              }
              

                上述全局异常处理器类,用于处理控制器中抛出的异常。这个类有三个方法:

              • handleException方法:使用@ResponseBody、@ResponseStatus和@ExceptionHandler注解,处理所有不可知的异常。如果发生异常,记录错误日志,并返回一个表示服务器错误的Result对象。

              • handleConstraintViolationException方法:使用@ResponseBody、@ResponseStatus和@ExceptionHandler注解,统一处理请求参数校验异常(普通传参)。如果发生ConstraintViolationException异常,记录错误日志,将异常信息转换为字符串列表,并返回一个表示请求错误的Result对象。

              • handleMethodArgumentNotValidException方法:使用@ResponseBody、@ResponseStatus和@ExceptionHandler注解,处理参数校验异常。如果发生MethodArgumentNotValidException异常,记录错误日志,将异常信息转换为字符串列表,并返回一个表示请求错误的Result对象。

                【Spring 校验】,在这里插入图片描述,第2张

                  如果喜欢的话,欢迎 🤞关注 👍点赞 💬评论 🤝收藏  🙌一起讨论
                  你的支持就是我✍️创作的动力!					  💞💞💞