SpringBoot Validation优雅的参数校验

前言:大多数项目中都需要后台对传过来的对象进行校验,所以经常需要写一些字段校验的代码,比如特殊字段非空、字段长度限制和邮箱格式验证等等。之前我们可能都是使用if…else…,写这些与业务逻辑关系不大的代码,不仅验证代码繁琐而且重复劳动,今天我们了解一下SpringBoot Validation的参数校验框架。

一、Hibernate-Validator简介

1.1、为什么需要参数校验

在日常的接口开发中,为了防止非法参数对业务造成影响,经常需要对接口的参数做校验,例如登录的时候需要校验用户名密码是否为空,创建用户的时候需要校验邮件、手机号码格式是否准确。靠代码对接口参数一个个校验的话就太繁琐了,代码可读性极差。

Validator框架就是为了解决开发人员在开发的时候少写代码,提升开发效率;Validator专门用来进行接口参数校验,例如常见的必填校验,email格式校验,用户名必须位于6到12之间 等等...

1.2、Hibernate-Validator参数校验


    org.hibernate
    hibernate-validator
    6.0.1.Final

Java API规范(JSR303)定义了Bean校验的标准validation-api,但没有提供实现。hibernate validator是对这个规范的实现,并增加了校验注解如@Email、@Length等。


二、SpringBoot 集成Hibernate-Validator参数校验

Spring Boot Validation是Spring Boot整合了Hibernate Validation的一个框架,作用是检验客户端向服务器端提交的请求参数的基本格式是否合法。

2.1、引入依赖

想要使用@validated需要引入Jar包,有两个方式,选择一种即可:

(1) 一般spring-boot-starter-web会自动传入hibernate-validator依赖6.0.1版本:


    org.springframework.boot
    spring-boot-starter-web

(2)如果没有,就手动传入


    org.springframework.boot
    spring-boot-starter-validation

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

  • POST、PUT请求,使用requestBody传递参数;

  • GET请求,使用requestParam/PathVariable传递参数。

2.2、requestBody参数校验

POST、PUT请求一般会使用requestBody传递参数,这种情况下,后端使用DTO对象进行接收。只要给DTO对象加上@Validated注解就能实现自动参数校验。比如,有一个保存User的接口,要求userName长度是2-10,account和password字段长度是6-20。

如果校验失败,会抛出MethodArgumentNotValidException异常,Spring默认会将其转为400(Bad Request)请求。

DTO表示数据传输对象(Data Transfer Object),用于服务器和客户端之间交互传输使用的。在Spring-Web项目中可以表示用于接收请求参数的Bean对象。

在DTO字段上声明约束注解

@Data
public class UserDTO {

    private Long userId;

    @NotNull
    @Length(min = 2, max = 10)
    private String userName;

    @NotNull
    @Length(min = 6, max = 20)
    private String account;

    //每个注解对应不同的校验规则,并可制定校验失败后的信息:
    @NotNull(message = "用户密码不能为空")
    @Length(min = 6, max = 20)
    private String password;
}

在方法参数上声明校验注解

@PostMapping("/save")
public Result saveUser(@RequestBody @Validated UserDTO userDTO) {
    // 校验通过,才会执行业务逻辑处理
    return Result.ok();
}

注意:如果你的验证实体前的注解是@RequestBody的话,无论你前端页面的数据再怎么完整,这个对象都是空的。后台会出现这个错误:Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported

解決方案:

一般我们在开发的过程中需要注意客户端发送请求(Request)时的Content-Type设置,特别是使用ajax的时候,如果设置得不准确,很有可能导致请求失败。比如在Spring中,如果接口使用了@RequestBody,Spring强大的自动解析功能,会将请求实体的内容自动转换为Bean,但前提是请求的Content-Type必须设置为application/json,否正就会返回415错误。
注:415 错误是 Unsupported media type,即不支持的媒体类型。

建议:

  • 如果是一个restful接口:一般将Content-Type设置为application/json; charset=UTF-8;

  • 如果是文件上传:一般Content-Type设置为multipart/form-data;

  • 如果普通表单提交:一般Content-Type设置为application/x-www-form-urlencoded

2.3、requestParam/PathVariable参数校验

GET请求一般会使用requestParam/PathVariable传参。如果参数比较多(比如超过6个),还是推荐使用DTO对象接收。

否则,推荐将一个个参数平铺到方法入参中。在这种情况下,必须在Controller类上标注@Validated注解,并在入参上声明约束注解(如@Min等)。如果校验失败,会抛出ConstraintViolationException异常。

代码示例如下:

@RequestMapping("/api/user")
@RestController
@Validated
public class UserController {
    // 路径变量
    @GetMapping("{userId}")
    public Result detail(@PathVariable("userId") @Min(10000000000000000L) Long userId) {
        // 校验通过,才会执行业务逻辑处理
        UserDTO userDTO = new UserDTO();
        userDTO.setUserId(userId);
        userDTO.setAccount("11111111111111111");
        userDTO.setUserName("xixi");
        userDTO.setAccount("11111111111111111");
        return Result.ok(userDTO);
    }

    // 查询参数
    @GetMapping("getByAccount")
    public Result getByAccount(@Length(min = 6, max = 20) @NotNull String  account) {
        // 校验通过,才会执行业务逻辑处理
        UserDTO userDTO = new UserDTO();
        userDTO.setUserId(10000000000000003L);
        userDTO.setAccount(account);
        userDTO.setUserName("hello");
        userDTO.setAccount("11111111111111111");
        return Result.ok(userDTO);
    }
}

2.4、全局统一异常处理

前面说过,如果校验失败,会抛出MethodArgumentNotValidException或者ConstraintViolationException异常。在实际项目开发中,通常会用统一异常处理来返回一个更友好的提示。比如我们系统要求无论发送什么异常,http的状态码必须返回对应业务码,由业务码去区分系统的异常情况。

import java.util.List;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class CommonExceptionHandler {

    //处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder("校验失败:");
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
        }
        String msg = sb.toString();
       return Result.fail(BusinessCode.参数校验失败, msg);
    }

    //处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
    @ExceptionHandler({ConstraintViolationException.class})
    public Result handleConstraintViolationException(ConstraintViolationException ex) {
        return Result.fail(BusinessCode.参数校验失败, ex.getMessage());
    }
}

三、Hibernate-Validator常用注解

SpringBoot Validation优雅的参数校验_第1张图片

 @Size、@Length、@Max、@Min注解的含义和区别

@Min 验证 Number 和 String 对象是否大等于指定的值

@Max 验证 Number 和 String 对象是否小等于指定的值

@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内

@Length(min=, max=) 验证字符串长度是否在给定的范围之内

max和min是对你填的“数字”是否大于或小于指定值,这个“数字”可以是number或者string类型。长度限制用length。


参考链接:

SpringBoot验证框架@Valid

SpringBoot 实现各种参数校验,写得太好了,建议收藏!

validate 抛出javax.validation.ConstraintViolationException异常 统一处理

你可能感兴趣的:(Java后端开发,Validator参数校验,优雅的参数校验)