统一校验的需求
- 前端请求后端接口传输参数,是在Controller中校验还是Service中校验?
- Controller中校验请求参数的合法性,包括:必填项校验、数据格式校验,比如:参数是否符合一定的日期格式等
- Controller中可以将校验的代码写成通用代码
- Service中要校验的是业务规则相关的内容,比如:课程已经审核通过,所以提交失败等
- Service中需要根据业务规则去校验,所以不方便写成通用代码
- 早在JavaEE6规范中,就定义了参数校验的规范,它就是JSR-303,它定义了Bean Validation,即对bean属性进行校验
- SpringBoot提供了JSR-303的支持,它就是spring-boot-stater-validation,它的底层使用Hibernate Validation,Hibernate Validation是Bean Validation的参考实现
- 所以我们打算在Controller层使用spring-boot-stater-validation完成对参数的基本合法性进行校验
引入spring-boot-starter-validation依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
校验注解
限制 | 说明 |
---|
@Null | 限制只能为null |
@NotNull | 限制制必须不为null |
@AssertFalse | 限制必须为false |
@AssertTrue | 限制必须为true |
@DecimalMax(value) | 限制必须为一个不大于指定值的数字 |
@DecimalMin(value) | 限制必须为一个不小于指定值的数字 |
@Digits(integer fraction | 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction |
@Future | 限制必须是一个将来的日期 |
@Max(value) | 限制必须为一个不大于指定值的数字 |
@Min(value) | 限制必须为一个不小于指定值的数字 |
@Past | 限制必须是一个过去的日期 |
@Pattern(value) | 限制必须符合指定的正则表达式 |
@Size(max, min) | 限制字符长度必须在min到max之间 |
@NotEmpty | 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0) |
@NotBlank | 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格 |
@Email | 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式 |
使用例子
分组(扩展)
public class ValidationGroups {
public interface Insert{}
public interface Update{}
public interface Delete{}
}
添加注解
@Data
@ApiModel(value = "AddCourseDto", description = "新增课程基本信息")
public class AddCourseDto {
@NotEmpty(message = "添加课程名称不能为空", groups = ValidationGroups.Insert.class)
@NotEmpty(message = "修改课程名称不能为空", groups = ValidationGroups.Update.class)
@ApiModelProperty(value = "课程名称", required = true)
private String name;
@NotEmpty(message = "适用人群不能为空")
@Size(min = 10, message = "适用人群内容过少,至少10个字符")
@ApiModelProperty(value = "适用人群", required = true)
private String users;
}
需要开启校验,在Controller方法中添加@Validated注解
@ApiOperation("新增课程基础信息接口")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody
@Validated 不需要分组
AddCourseDto addCourseDto) {
Long companyId = 22L;
return courseBaseInfoService.createCourseBase(companyId, addCourseDto);
}
抛出异常
如果校验出错,Spring会抛出MethodArgumentNotValidException异常,我们需要在全局异常处理器中捕获异常,解析出异常信息
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public RestErrorResponse doMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
BindingResult bindingResult = exception.getBindingResult();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
StringBuffer stringBuffer = new StringBuffer();
fieldErrors.forEach(fieldError -> stringBuffer.append(fieldError.getDefaultMessage()).append(","));
log.error(stringBuffer.toString());
return new RestErrorResponse(stringBuffer.toString());
}
}