前言:大多数项目中都需要后台对传过来的对象进行校验,所以经常需要写一些字段校验的代码,比如特殊字段非空、字段长度限制和邮箱格式验证等等。之前我们可能都是使用if…else…,写这些与业务逻辑关系不大的代码,不仅验证代码繁琐而且重复劳动,今天我们了解一下SpringBoot Validation的参数校验框架。
在日常的接口开发中,为了防止非法参数对业务造成影响,经常需要对接口的参数做校验,例如登录的时候需要校验用户名密码是否为空,创建用户的时候需要校验邮件、手机号码格式是否准确。靠代码对接口参数一个个校验的话就太繁琐了,代码可读性极差。
Validator框架就是为了解决开发人员在开发的时候少写代码,提升开发效率;Validator专门用来进行接口参数校验,例如常见的必填校验,email格式校验,用户名必须位于6到12之间 等等...
org.hibernate
hibernate-validator
6.0.1.Final
Java API规范(JSR303)定义了Bean校验的标准validation-api,但没有提供实现。hibernate validator是对这个规范的实现,并增加了校验注解如@Email、@Length等。
Spring Boot Validation是Spring Boot整合了Hibernate Validation的一个框架,作用是检验客户端向服务器端提交的请求参数的基本格式是否合法。
想要使用@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传递参数。
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
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);
}
}
前面说过,如果校验失败,会抛出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());
}
}
@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异常 统一处理