一、常用注解
| 注解 | 分类 | 含义 | 描述 |
|---|---|---|---|
| @NotEmpty | 空和非空 | 集合不能为空 | 用在集合上面,一般用来校验List类型(不能注释枚举),而且长度必须大于0 |
| @NotBlank | String不能为空 | 用在String上面,一般用来校验String类型不能为空,而且调用trim()后,长度必须大于0。 | |
| @NotNull | 所有类型不能为null | 用在所有类型上面,一般用来校验Integer类型不能为空,但可以为empty。 | |
| @Null | 元素必须为null | 元素必须为null | |
| @AssertTrue | Boolean值 | 元素必须为true | 元素必须为true |
| @AssertFalse | 元素必须是false | 元素必须是false | |
| @Min(value) | 数值 | 最小值 | 元素必须是一个数字,其值必须大于等于指定的最小值 |
| @Max(value) | 最大值 | 元素必须是一个数字,其值必须小于等于指定的最大值 | |
| @DecimalMin(value) | 最小值(包含) | 元素必须是一个数字,其值必须大于等于指定的最小值 | |
| @DecimalMax(value) | 最大值(包含) | 元素必须是一个数字,其值必须小于等于指定的最大值 | |
| @Negative | 负整数 | 负整数 | |
| @NegativeOrZero | 负整数或零 | 负整数或零 | |
| @Positive | 正整数 | 正整数 | |
| @PositiveOrZero | 正整数或零 | 正整数或零 | |
| @Digits(integer,fraction) | 值范围 | 元素必须是一个数字,其值必须在可接受的范围内 | |
| @Size(max,min) | 范围 | 长度范围 | 元素的大小必须在指定的范围内(可以处理数组、集合) |
| @Length | 字符串长度 | 元素的大小必须在指定的范围内(只能处理字符串) | |
| @Range | 值范围 | 元素必须在合理的范围内 | |
| @Past | 日期 | 过去日期 | 元素必须是一个过去的日期 |
| @PastOrPresent | 过去日期或当前日期 | ||
| @Future | 未来日期 | 元素必须是一个将来的日期 | |
| @FutureOrPresent | 未来日期或当前日期 | ||
| @Pattern(value) | 其他 | 正则匹配 | 元素必须符合指定的正则表达式 |
| 邮箱 | 元素必须是电子邮箱地址 |
二、@Valid与@Validated的使用与区别
Spring Validation验证框架对参数的验证机制提供了@Validated(Spring's JSR-303规范,是标准JSR-303的一个变种),javax提供了@Valid(标准JSR-303规范),配合BindingResult可以直接提供参数验证结果。
@Valid、@Validated 注解可以实现数据的验证,你可以定义实体,在实体的属性上添加校验规则,校验规则如上@NotEmpty,@NotBlank,@NotNull等等的注解就是,不加上就不会生效!
@Valid 包位置:javax.validation
@Validated 包位置 org.springframework.validation.annotation
是@Valid 的一次封装,是Spring提供的校验机制使用。
2.1@Valid与@Validated区别:
相同点: 在检验Controller的入参是否符合规范时,使用@Validated或者@Valid都可以实现,并且需要传入BindingResult对象,用于获取校验失败情况下的反馈信息,如下代码:
@PostMapping("register")
//@Valid这两个用哪个效果都一样
public JSONResult register(@Validated @RequestBody RegisterVo registerVo, BindingResult bindingResult){
//会把校验失败情况下的反馈信息
if (bindingResult.hasErrors()) {
System.out.println(bindingResult.getFieldError().getDefaultMessage());
return JSONResult.error().message(bindingResult.getFieldError().getDefaultMessage());
}
memberService.register(registerVo);
return JSONResult.ok();
}
不同点:2个方面如下
1、分组上:
@Valid 不支持分组, @Validated则支持分组验证。
2、使用位置:
@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上
@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上
2.2嵌套验证
class Person {
@NotNull
private Integer id;
@NotNull
/**
* @Validated不允许使用在成员属性上,所以只能用@Valid
* 如果不加 @Valid 就不会对 Son类进行校验,只会判断 @NotNull,List<Son>是否为空,并不会Son类里面的成员属性
*/
@Valid //嵌套验证
private List<Son> son;
}
class Son {
@NotNull
private Integer sid;
@NotBlank(message = "姓名不能为空")
private String name;
}
class MemberController {
@ApiOperation(value = "会员注册")
@PostMapping("register")
//使用@Validated或者@Valid都可以,这样就完成了嵌套验证
public JSONResult register(@Validated @RequestBody Person person, BindingResult bindingResult){
if (bindingResult.hasErrors()) {
System.out.println(bindingResult.getFieldError().getDefaultMessage());
return JSONResult.error().message(bindingResult.getFieldError().getDefaultMessage());
}
return JSONResult.ok();
}
}
三、在Service层中使用@Vaild
3.1对象校验
3.1.1在接口上入参上增加@vaild注解
3.1.2在接口实现类上增加@Vaildated注解,在入参增加@vaild注解
3.1.3在入参实体类中增加相关注解
3.2方法及值校验
在接口上增加@Validated注解,在入参上增加对应校验注解
@Validated(Default.class)
public interface HelloService {
Object hello(@NotNull @Min(10) Integer id, @NotNull String name);
}
public class HelloServiceImpl implements HelloService {
@Override
public Object hello(Integer id, String name) {
return null;
}
}
四、全局异常处理
如果我们直接使用e.getMessage()获得报错信息的话,报错信息太长了,并不能直接返回给前端,前端也看不懂,所以需要进行处理。
也可使用AOP切面实现
异常类型:
| BindException | 表单提交有效,对于以json格式提交将会失效 |
|---|---|
| MethodArgumentNotValidException | 前段以json格式有效 |
| ConstraintViolationException | jsr 规范中的验证异常,嵌套检验问题 |
4.1参考一
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseData exceptionHandler(MethodArgumentNotValidException ex) {
System.out.println("全局异常处理捕捉");
BindingResult result = ex.getBindingResult();
String message = "";
if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
if (errors != null) {
errors.forEach(p -> {
FieldError fieldError = (FieldError) p;
log.error("Data check failure : object{" + fieldError.getObjectName() + "},field{" + fieldError.getField() +
"},errorMessage{" + fieldError.getDefaultMessage() + "}");
});
if (errors.size() > 0) {
FieldError fieldError = (FieldError) errors.get(0);
message = fieldError.getDefaultMessage();
}
}
}
message = "".equals(message) ? "参数不正确!":message;
return ResponseData.defaultFail(message);
}
}
4.2参考二
import cn.hutool.core.util.StrUtil;
import com.course.commons.dto.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Title:统一异常,返回json
* Description:
* @author WZQ
* @version 1.0.0
* @date 2021/4/22
*/
@RestControllerAdvice(annotations = {RestController.class, Controller.class})
@Slf4j
public class BaseExceptionHandler {
/**
* 空参异常处理
* @param ex
* @param request
* @return
*/
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseResult<Void> bindException(BindException ex, HttpServletRequest request) {
log.warn("BindException:", ex);
try {
// 拿到@NotNull,@NotBlank和 @NotEmpty等注解上的message值
String msg = Objects.requireNonNull(ex.getBindingResult().getFieldError()).getDefaultMessage();
if (StrUtil.isNotEmpty(msg)) {
// 自定义状态返回
return new ResponseResult<>(ResponseResult.CodeStatus.FAIL, msg);
}
} catch (Exception ignored) {
}
// 参数类型不匹配检验
StringBuilder msg = new StringBuilder();
List<FieldError> fieldErrors = ex.getFieldErrors();
fieldErrors.forEach((oe) ->
msg.append("参数:[").append(oe.getObjectName())
.append(".").append(oe.getField())
.append("]的传入值:[").append(oe.getRejectedValue()).append("]与预期的字段类型不匹配.")
);
return new ResponseResult<>(ResponseResult.CodeStatus.FAIL, msg.toString());
}
/**
* jsr 规范中的验证异常,嵌套检验问题
* @param ex
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseResult<Void> constraintViolationException(ConstraintViolationException ex, HttpServletRequest request) {
log.warn("ConstraintViolationException:", ex);
Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
String message = violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(";"));
// ConstraintViolation<?> violation = violations.iterator().next();
// String path = ((PathImpl) violation.getPropertyPath()).getLeafNode().getName();
// String message2 = String.format("%s:%s", path, violation.getMessage());
return new ResponseResult<>(ResponseResult.CodeStatus.FAIL, message);
}
/**
* spring 封装的参数验证异常, 在controller中没有写result参数时,会进入
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseResult<Void> methodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request) {
log.warn("MethodArgumentNotValidException:", ex);
return new ResponseResult<>(ResponseResult.CodeStatus.FAIL, Objects.requireNonNull(ex.getBindingResult().getFieldError()).getDefaultMessage());
}
}
五、Maven依赖
<!--第一种方式导入校验依赖-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!--第二种方式导入校验依赖-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
六、总结
1、常用校验注解@NotEmpty,@NotBlank,@NotNull,需要搭配@Valid或@Validated使用
2、@Valid或@Validated 中有 BindingResult,可以获取校验失败情况下的反馈信息,如果不添加,默认情况下会抛出对应的异常。
3、@Validated无法单独提供嵌套验证功能。不能用在成员属性上,能配合嵌套验证注解@Valid进行嵌套验证。
@Valid无法单独提供嵌套验证功能。能够用在成员属性上,能配合嵌套验证注解@Valid进行嵌套验证。