文章目录
validator内置注解
注解 | 详细信息 |
---|---|
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素必须不为 null |
@AssertTrue | 被注释的元素必须为 true |
@AssertFalse | 被注释的元素必须为 false |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max, min) | 被注释的元素的大小必须在指定的范围内 |
@Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
Hibernate Validator 附加的 constraint
注解 | 详细信息 |
---|---|
@Email | 被注释的元素必须是电子邮箱地址 |
@Length | 被注释的字符串的大小必须在指定的范围内 |
@NotEmpty | 被注释的字符串的必须非空 |
@Range | 被注释的元素必须在合适的范围内 |
@NotBlank | 验证字符串非null,且长度必须大于0 |
注意:
- @NotNull 适用于任何类型被注解的元素必须不能与NULL
- @NotEmpty 适用于集合或者数组不能为Null且长度必须大于0
- @NotBlank 只能用于String上面 不能为null,调用trim()后,长度必须大于0
这里写一个手机号的正则校验(这只是一个简单手机号正则校验)—建议如果想实现复杂的参数校验可以自定义的约束(后面会写)
/**
* 手机号
*/
@NotNull(message = "手机号不能为空")
@NotBlank(message = "手机号不能为空")
@Pattern(regexp ="^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
private String mobileNo;
@Validated
这个注解来自 package org.springframework.validation.annotation 包下
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
可以 使用在类上、方法上、参数上
若要实现单一参数的效验
(我感觉若是使用restful风格,这个好像没用,因为若不传递参数会直接报错)
@Validated注解必须设置在类上
栗子:(单个参数异常处理也配置在了全局异常处理里面了)
/**
* 根据id查询用户
* @param id
* @return
*/
@GetMapping("/find")
@ApiOperation("查找用户")
public User find(@NotBlank(message = "id不能为空") String id){
User user = new User();
user.setId(Long.parseLong(id));
return user;
}
若要实现javabean对象效验
@validated注解必须设置在参数前面
栗子:
/**
* 添加商品
* @param cartDTO
*/
@PostMapping
@ApiOperation("向购物车中添加商品")
public Integer addCart(@RequestBody @Validated CartDTO cartDTO){
return cartService.addCart(cartDTO);
}
我这里json传递的是 {}
响应给我的json是(若想让响应结果像我这样还需要设置全局异常处理后面会说)
{
"code": 1002,
"msg": "参数效验失败",
"data": [
"商品id不能为空",
"商品类型不能为空",
"商品属性id不能为空",
"用户id不能为空"
]
@Data
public class CartDTO {
@ApiModelProperty(value = "购物车表ID")
@NotNull(message = "更新时字段id必填",groups = {Update.class})
private Long id;
@ApiModelProperty(value = "用户ID")
@NotNull(message = "用户id不能为空")
private Long uid;
@ApiModelProperty(value = "类型")
@NotBlank(message = "商品类型不能为空")
private String type;
@ApiModelProperty(value = "商品ID")
@NotNull(message = "商品id不能为空")
private Long productId;
@ApiModelProperty(value = "商品属性")
@NotBlank(message = "商品属性id不能为空")
private String productAttrUnique;
@ApiModelProperty(value = "商品数量")
@Min(message = "商品数量不能小于0",value = 0)
private Integer cartNum;
@ApiModelProperty(value = "0 = 未购买 1 = 已购买")
private Boolean isPay;
@ApiModelProperty(value = "是否为立即购买")
private Boolean isNew;
@ApiModelProperty(value = "拼团id")
private Integer combinationId;
@ApiModelProperty(value = "秒杀产品ID")
private Integer seckillId;
@ApiModelProperty(value = "砍价id")
private Integer bargainId;
}
@Valid
来自 package javax.validation; 包下
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
它可以使用在使用在类、字段、构造器、参数上
若要实现javabean对象效验和@Validated用法是一样的(这里就不举例子了)
@Validated和@Valid的区别
1 使用@Validated可以实现分组校验
栗子:
1、首先你要创建分组(我这里是创建了两个,新建验证、更新验证)而且必须要继承Default(javax.validation.groups.Default;)接口
package cn.newhopedairy.newshop.mall.common.cart.groups;
import javax.validation.groups.Default;
public interface Create extends Default {
}
package cn.newhopedairy.newshop.mall.common.cart.groups;
import javax.validation.groups.Default;
public interface Update extends Default {
}
2、在字段上配置分组属性(可以配置多个)
@Data
public class CartDTO {
@ApiModelProperty(value = "购物车表ID")
@NotNull(message = "更新时字段id必填",groups = {Update.class})
@NotNull(message = "新增时字段id必填",groups = {Create.class})//这里我只是举个例子,实际中不会这样验证
private Long id;
......
}
3、在验证的入口处(一般在controller的方法中)
@RestController
@Api("购物车接口")
@RequestMapping("/cart")
@Validated
public class CartController {
/**
* 添加商品
* @param cartDTO
*/
@PostMapping
@ApiOperation("向购物车中添加商品")
//
public Integer addCart(@RequestBody @Validated(value = {Create.class}) CartDTO cartDTO){
return cartService.addCart(cartDTO);
}
/**
* 修改商品数量
* @param cartDTO
* @return
*/
@PatchMapping
@ApiOperation("修改购物车中的商品数据")
public Integer updateCart(@RequestBody @Validated(Update.class) CartDTO cartDTO){
return cartService.updateCart(cartDTO);
}
}
4、测试
调用添加商品的方法
http://localhost:8080/cart
请求时的json数据为:
{
"bargainId": 0,
"cartNum": 0,
"combinationId": 0,
"isNew": true,
"isPay": true,
"productAttrUnique": "string",
"productId": 0,
"seckillId": 0,
"type": "string",
"uid": 0
}
响应的json数据为:
{
"code": 1002,
"msg": "参数效验失败",
"data": [
"新增时字段id必填"
]
}
调用修改商品的方法
http://localhost:8080/cart
请求时的json数据为:
{
"bargainId": 0,
"cartNum": 0,
"combinationId": 0,
"isNew": true,
"isPay": true,
"productAttrUnique": "string",
"productId": 0,
"seckillId": 0,
"type": "string",
"uid": 0
}
响应的json数据为:
{
"code": 1002,
"msg": "参数效验失败",
"data": [
"更新时字段id必填"
]
}
这样就是实现了分组效验的效果
“data”: [
“更新时字段id必填”
]“data”: [
“新增时字段id必填”
]
2 @Validated和@Valid配合使用key实现嵌套验证
@Valid和@Valid配合使用也可以实现使用方式差不多。
@PostMapping
@ApiOperation("查找用户")
public User add(@RequestBody @Validated User user){
return user;
}
@Data
@ApiModel(value="User对象", description="")
public class User implements Serializable {
@ApiModelProperty(value = "年龄")
@Min(value = 1,message = "年龄不能小于1")
private Integer age;
@ApiModelProperty(value = "邮箱")
private String email;
@Valid
private Car car;
}
@Data
@ApiModel(value="User对象", description="")
public class Car {
@NotBlank(message = "车的颜色不能为空")
private String color;
@Min(value = 5,message = "车长必须超过5米")
private Integer type;
}
请求参数:
{
"age": 10,
"car": {"color": "",
"type": 10},
"email": "string",
"id": 0,
"name": "string"
}
响应参数:
{
"code": 1002,
"msg": "参数效验失败",
"data": [
"车的颜色不能为空"
]
}
单参数校验
Springboot使用JSR-303 Validation进行验证,大部分博客都只提及对Controller中的dto对象进行属性校验。本文是介绍如何对Controller中的方法的单一参数进行校验,步骤如下。
1. Controller添加注解@Validated
@RestController
@RequestMapping("/test")
@Validated
public class TestController {}
2. 单一参数校验
使用@Min等标签校验参数。
@PostMapping("/list")
@ResponseBody
public Result<String> getBookList(@Min(1) Integer pageNum,@Min(1) Integer pageSize, @RequestBody @Validated TestDto dto) {
...
}
自定义异常
package com.example.hk_jsr303.exception;
import com.example.hk_jsr303.result.ResultVO;
import lombok.Getter;
import lombok.Setter;
/**
* @Classname MyException
* @Description TODO
* @Date 2020/12/9 23:46
* @Created by hekun
*/
@Getter
@Setter
public class MyException extends RuntimeException {
private int code;
private String msg;
public MyException(){}
public MyException(ResultVO resultVO) {
this.code=resultVO.getCode();
this.msg=resultVO.getMsg();
}
}
定义全局异常处理
更具体的定义可以看一下这篇blog:
https://juejin.cn/post/6872226869605826573#heading-6
package com.example.hk_jsr303.exception;
import com.example.hk_jsr303.result.ResultVO;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @Classname GlobalExceptionHandler
* @Description TODO
* @Date 2020/12/9 15:11
* @Created by hekun
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 方法参数错误异常
* @param e
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResultVO<Object> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
//log.error("方法参数错误异常");
List<String> list=new ArrayList<>();
// 从异常对象中拿到ObjectError对象
if (!e.getBindingResult().getAllErrors().isEmpty()){
for(ObjectError error:e.getBindingResult().getAllErrors()){
list.add(error.getDefaultMessage().toString());
}
}
// 然后提取错误提示信息进行返回
return new ResultVO(1002,"参数效验失败",list);
}
/**
* 单个参数异常处理
*
* @param ex
* @return
*/
@ExceptionHandler(value = ConstraintViolationException.class)
public Object constraintViolationException(ConstraintViolationException ex) {
// 获取具体的错误信息
Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
// 打印数据
violations.forEach(e -> System.out.println(e.getMessage()));
return "单个-请求参数错误";
}
}
//下面这个结果类要使用到
@Getter
@Setter
public class ResultVO<T> {
/**
* 状态码,比如1000代表响应成功
*/
private int code;
/**
* 响应信息,用来说明响应情况
*/
private String msg;
/**
* 响应的具体数据
*/
private T data;
public ResultVO(int code,String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public ResultVO(int code,String msg) {
this.code = code;
this.msg = msg;
}
}
自定义约束
一般情况内置的约束一般是够用了,如果有别的情况也是可以自己定义约束条件的
假设我们要给车设置一个颜色,而且颜色只能设置为red或者green
1、注意:@ListValue这个注解是我们自定义的
@Data
@ApiModel(value="User对象", description="")
public class Car {
@NotBlank(message = "车的颜色不能为空")
@ListValue(vals = {"red","green"},message = "车的颜色不对")
private String color;
@Min(value = 5,message = "车长必须超过5米")
private Integer type;
}
2、我们要创建这个注解@ListValue,我们可以模仿官方写的注解比如@Min
package com.example.hk_jsr303.groups;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = {ListValueConstraintValidator.class})//自定义的约束校验器 ---很重要
//自定义的约束校验器 作用我们要在里面写一下校验规则 isValid() 方法返回true表示校验成功
@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListValue {
//错误信息的提示
String message() default "{com.sjl.common.valid.ListValue.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
//自定义值的类型
String[] vals() default {};
}
3、写我们自己的约束校验器ListValueConstraintValidator ,这里要注意继承ConstraintValidator
package com.example.hk_jsr303.groups;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;
/**
* @Classname ListValueConstraintValidator
* @Description TODO
* @Date 2020/12/9 22:58
* @Created by hekun
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,String> {
private static Set<String> set = new HashSet<>();
@Override
public void initialize(ListValue constraintAnnotation) {
for (String val : constraintAnnotation.vals()) {
set.add(val);
}
}
/**
* 判断是否通过校验
*
* @param value 传入的值
* @param context
* @return
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
4、进行测试
/**
* 添加车辆
* @param car
* @return
*/
@PostMapping("/car")
@ApiOperation("新增车辆")
public Car addCar(@RequestBody @Validated Car car){
return car;
}
请求json:
{
"color": "red1",
"type": 10
}
响应json:
{
"code": 1002,
"msg": "参数效验失败",
"data": [
"车的颜色不对"
]
}
参考文档
Bean Validation 技术规范特性概述
)
https://www.jianshu.com/p/60aadd416eb4
https://juejin.cn/post/6844904118536912904#heading-7
https://juejin.cn/post/6844904016380461070#heading-6
https://juejin.cn/post/6872226869605826573#heading-7
https://juejin.cn/post/6844904133296652296#heading-6
https://www.cnblogs.com/songjilong/p/12663564.html
https://blog.csdn.net/justry_deng/article/details/86571671?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-8.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-8.control
————————————————
原文链接:xiaoxiaokunkunkun - Springboot参数校验