添加依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
|
注解
注解及意思
注解 |
功能 |
@AssertFalse |
必须是false |
@AssertTrue |
必须是true |
@DecimalMax |
小于等于给定的值 |
@DecimalMin |
大于等于给定的值 |
@Digits |
可设定最大整数位数和最大小数位数 |
@Email |
必须为一个邮箱 |
@Future |
必须是将来的时间 |
@FutureOrPresent |
当前或将来时间 |
@Max |
最大值 |
@Min |
最小值 |
@Negative |
负数(不包括0) |
@NegativeOrZero |
负数或0 |
@NotBlank |
必须包含一个非空字符,不能是空字符串 |
@NotEmpty |
不能为空,可以为空字符串 |
@NotNull |
不为null |
@Null |
为null |
@Past |
必须是过去的时间 |
@PastOrPresent |
必须是过去的时间,包含现在 |
@Pattern |
匹配正则表达式 |
@Positive |
正数 |
@PositiveOrZero |
正数或0 |
@Size |
校验容器的元素个数 |
注解通用属性及意思
1 2 3 4 5
| # message:自定义消息 String message() default "{javax.validation.constraints.Null.message}";
# groups:在进行分组校验时候使用 Class<?>[] groups() default { };
|
基本使用
实体类
再属性上面添加字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Data @ApiModel("用户信息") public class UserEntity {
@ApiModelProperty("用户id") private Integer id;
@NotEmpty @ApiModelProperty("用户名") private String username;
@NotBlank @ApiModelProperty("用户密码") private String password; }
|
控制类
在控制类接收参数上面添加@Valid
注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Api(tags = "用户信息") @RestController @RequestMapping("user") public class UserController {
@Resource private UserService userService;
@ApiOperation(value = "添加信息", httpMethod = "POST", response = ResponseUtils.class, produces = "application/json") @PostMapping public ResponseUtils<Boolean> add(@Valid @RequestBody UserEntity userEntity) { Boolean isSuccess = userService.add(userEntity); return new ResponseUtils<Boolean>().success(isSuccess ? "添加成功" : "添加失败", isSuccess); } }
|
测试
测试数据
1 2 3 4
| { "password": "", "username": "" }
|
测试结果
1 2 3 4 5
| { "code": 400, "msg": "Validation failed for argument [0] in public com.xiaofei.validation.utils.ResponseUtils<java.lang.Boolean> com.xiaofei.validation.controller.UserController.add(com.xiaofei.validation.entity.UserEntity) with 2 errors: [Field error in object 'userEntity' on field 'username': rejected value []; codes [NotEmpty.userEntity.username,NotEmpty.username,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userEntity.username,username]; arguments []; default message [username]]; default message [不能为空]] [Field error in object 'userEntity' on field 'password': rejected value []; codes [NotBlank.userEntity.password,NotBlank.password,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userEntity.password,password]; arguments []; default message [password]]; default message [不能为空]] ", "data": null }
|
统一返回结果
由于上面的测试结果展示的数据不方便调试,由此可以统一返回测试结果,方便调试。测试未通过异常类型MethodArgumentNotValidException
统一返回异常处理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @RestControllerAdvice public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(MethodArgumentNotValidException.class) public ResponseUtils<Object> validException(MethodArgumentNotValidException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("\n请求地址:{}\n发生系统异常\n数据校验出现问题:{}\n异常类型:{}", requestURI, e.getMessage(), e.getClass());
Map<String, String> resultMap = new HashMap<>(); BindingResult bindingResult = e.getBindingResult(); bindingResult.getFieldErrors().forEach(fieldError -> { resultMap.put(fieldError.getField(), fieldError.getDefaultMessage()); });
return new ResponseUtils<Object>().error(400, "数据校验出现问题", resultMap); } }
|
测试
测试数据
1 2 3 4
| { "password": "", "username": "" }
|
测试结果
1 2 3 4 5 6 7 8
| { "code": 400, "msg": "数据校验出现问题", "data": { "password": "不能为空", "username": "不能为空" } }
|
分组校验
当不同请求进行不同校验时,可以使用分组校验进行区分。当新增时,可以指定哪些校验生效。当修改时,可以指定哪些校验生效。实体类上校验注解,设置groups
,在控制类上面@Valid
改为@Validated
注解,并设置其value值
添加分组接口类
接口内容为空
在实体类属性上添加分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Data @ApiModel("用户信息") @TableName(value = "user") public class UserEntity implements Serializable {
@ApiModelProperty("用户id") @Null(message = "新增id必须为空", groups = {AddGroup.class}) private Integer id;
@NotEmpty(message = "用户名不能为空", groups = {AddGroup.class, UpdateGroup.class}) @ApiModelProperty("用户名") private String username;
@NotBlank @ApiModelProperty("用户密码") private String password;
@TableField(exist = false) private static final long serialVersionUID = 1L; }
|
在控制类上指定注解
当使用@Validated
指定了分组校验后,未指定分组的校验注解将不会生效
1 2 3 4 5 6
| @ApiOperation(value = "添加信息", httpMethod = "POST", response = ResponseUtils.class, produces = "application/json") @PostMapping public ResponseUtils<Boolean> add(@Validated(value = {AddGroup.class}) @RequestBody UserEntity userEntity) { Boolean isSuccess = userService.add(userEntity); return new ResponseUtils<Boolean>().success(isSuccess ? "添加成功" : "添加失败", isSuccess); }
|
测试
自定义校验注解
- 编写一个自定义的校验注解
- 编写一个自定义的校验器
- 关联自定义的校验器和自定义的校验注解
添加依赖
如果使用了spring-boot-starter-validation
则不需要添加下面依赖,spring-boot-starter-validation
自带该依赖
1 2 3 4
| <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> </dependency>
|
自定义校验注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Documented @Constraint(validatedBy = {ListValueConstraintValidator.class}) @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) public @interface ListValue {
String message() default "必须为指定的值";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int[] values() default {}; }
|
编写校验器
编写一个类,实现ConstraintValidator
接口,实现initialize
和isValid
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
private final Set<Integer> set = new HashSet<>();
@Override public void initialize(ListValue listValue) { int[] values = listValue.values(); for (int value : values) { set.add(value); } }
@Override public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) { return set.contains(value); } }
|
使用
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Data @ApiModel("用户信息") @TableName(value = "user") public class UserEntity implements Serializable {
@ApiModelProperty("用户id") @Null(message = "新增id必须为空", groups = {AddGroup.class}) private Integer id;
@NotEmpty(message = "用户名不能为空", groups = {AddGroup.class, UpdateGroup.class}) @ApiModelProperty("用户名") private String username;
@NotBlank @ApiModelProperty("用户密码") private String password;
@ListValue(values = {1, 2}) @ApiModelProperty("状态") @TableField(exist = false) private Integer status;
@TableField(exist = false) private static final long serialVersionUID = 1L; }
|
控制类
1 2 3 4 5 6
| @ApiOperation(value = "添加信息", httpMethod = "POST", response = ResponseUtils.class, produces = "application/json") @PostMapping public ResponseUtils<Boolean> add(@Validated @RequestBody UserEntity userEntity) { Boolean isSuccess = userService.add(userEntity); return new ResponseUtils<Boolean>().success(isSuccess ? "添加成功" : "添加失败", isSuccess); }
|
常用的JSR303校验
下面的模板都是基于分组校验实现的,如果不使用分组校验,直接删除分组校验部分即可,下列可能不准确,参考即可
IP
1 2 3
| @Pattern(regexp = "((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])(?::(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?", message = "IP格式错误", groups = {AddGroup.class, UpdateGroup.class})
|
端口
1 2
| @Min(value = 1, message = "端口格式错误,必须为1-65535", groups = {AddGroup.class, UpdateGroup.class}) @Max(value = 65535, message = "端口格式错误,必须为1-65535", groups = {AddGroup.class, UpdateGroup.class})
|
其他
1 2
| @Null(message = "新增时不能携带id", groups = {AddGroup.class}) @NotBlank(message = "修改时必须携带id", groups = {UpdateGroup.class})
|