实际项目中不仅仅前端需要做必填项等校验,为防止非法参数对业务造成影响,后端也需要对相关参数做校验,接下来就学习一下在Springboot项目中如何对参数进行校验。本文Springboot版本为2.6.8
引入依赖
如果Springboot版本小于2.3.x,spring-boot-starter-web会自动传入hibernate-validator依赖。如果Springboot版本大于2.3.x,则需要手动引入依赖: 温馨提示:7.x.x版本可能会不起效
<!--https://mvnrepository.com/artifact/org.hibernate/hibernate-validator --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>6.2.0.Final</version></dependency>
或者直接引入springboot的场景启动器
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><version>2.7.0</version></dependency>
@Validated和@Valid区别
@Validated对@Valid进行了二次封装,但是二者有以下的区别:
- @Validated提供分组功能,可以在参数验证时,根据不同的分组采用不同的验证机制。@Valid没有分组功能
- @Validated用在类型、方法和方法参数上。但不能用于成员属性(field),@Valid可以用在方法、构造函数、方法参数和成员属性(field)上
- 一个待验证的pojo类,其中还包含了待验证的对象属性,需要在待验证对象上注解@Valid,才能验证待验证对象中的成员属性,这里不能使用@Validated
统一异常处理
如果校验不通过会报MethodArgumentNotValidException或者 ConstraintViolationException】
publicclassParamException { MethodArgumentNotValidException.class}) ({HttpStatus.BAD_REQUEST) (publicResultReturnhandleMethodArgumentNotValidException(MethodArgumentNotValidExceptionex) { BindingResultbindingResult=ex.getBindingResult(); StringBuildersb=newStringBuilder("校验失败:"); for (FieldErrorfieldError : bindingResult.getFieldErrors()) { sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", "); } Stringmsg=sb.toString(); returnResultReturnUtil.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),msg); } ConstraintViolationException.class}) ({HttpStatus.BAD_REQUEST) (publicResultReturnhandleConstraintViolationException(ConstraintViolationExceptionex) { returnResultReturnUtil.fail(ErrorCodeEnum.PARAM_ERROR.getCode(),ex.getMessage()); } }
我们可以把要返回的错误信息定义在枚举中
publicenumErrorCodeEnum { PARAM_ERROR(10001,"参数错误"); privateIntegercode; privateStringmsg; ErrorCodeEnum(Integercode, Stringmsg) { this.code=code; this.msg=msg; } }
再来个统一返回格式
publicclassResultReturnimplementsSerializable { privatestaticfinallongserialVersionUID=5805792987639183304L; privateIntegercode; privateStringmsg; privateObjectdata; publicResultReturn(){ super(); } publicResultReturn(Integercode, Stringmsg){ this.code=code; this.msg=msg; } publicResultReturn(Integercode, Stringmsg, Objectdata){ this.code=code; this.msg=msg; this.data=data; } }
publicclassResultReturnUtil { /*** 成功 返回默认码值* @param msg* @return*/publicstaticResultReturnsuccess(Stringmsg){ returnnewResultReturn(0,msg); } /*** 成功 返回自定义码值* @param code* @param msg* @return*/publicstaticResultReturnsuccess(Integercode, Stringmsg){ returnnewResultReturn(code,msg); } publicstaticResultReturnsuccess(Stringmsg, Objectdata){ returnnewResultReturn(0,msg,data); } publicstaticResultReturnfail(Stringmsg){ returnnewResultReturn(-1,msg); } publicstaticResultReturnfail(Integercode, Stringmsg){ returnnewResultReturn(code,msg); } }
RequestBody参数校验
当我们使用POST、PUT请求时会使用 @RequestBody+实体类 来接收参数。此时只需要给实体类加上@Validated
注解就能实现自动参数校验。
publicclassTestController { "/addUser") (publicBooleanaddUser(Useruser){ //忽略service处理相关业务returntrue; } }
那么具体到每个参数的校验则需要在实体类中处理。
publicclassUser { privateLongid; message="用户账号不能为空") (min=4, max=10,message="账号长度为4-10位") (privateStringuserName; message="密码不能为空") (min=6,max=12,message="密码长度为6-12位") (privateStringpassword; message="邮箱不能为空") (message="邮箱格式错误") (privateStringemail; /*** 性别 1为男生 2为女生*/value=1,message="最小值为1") (value=2,message="最大值为2") (privateIntegersex; }
先请求一次试试
requestParam/PathVariable
参数校验
这种方式下参数都较少,必须在Controller
类上标注@Validated
注解,并在入参上声明约束注解
接下来请求试一下
特殊情况
也是上文提到的,pojo参数中有对象属性,那么要对这个对象中的属性校验该怎么做?
比如我这里有个学生类
publicclassStudent { message="用户名不能为空") (privateStringname; value=10, message="年龄不能小于10岁") (privateIntegerage; message="邮箱格式错误") (privateStringemail; message="班级名不能为空") (privateStringclassName; message="任课老师不能为空") (min=1, message="至少有一个老师") (privateList<Teacher>teachers; }
publicclassTeacher { message="老师姓名不能为空") (privateStringteacherName; value=1, message="学科类型从1开始计算") (privateIntegertype; }
测试一下
很明显这个type的值并不符合要求, 需要在学生类的教师属性上加上@Valid注解
参数校验注解大全
注解 | 作用类型 | 解释 | null是否 能通过验证 |
@AssertFalse | Boolean、boolean | 该字段值为false时,验证才能通过 | YES |
@AssertTrue | Boolean、boolean | 该字段值为true时,验证才能通过 | YES |
@DecimalMax | 数字类型(原子和包装) | 验证小数的最大值@DecimalMax(value="12.35")privatedouble money;
|
YES |
@DecimalMin | 数字类型(原子和包装) | 验证小数的最小值 | YES |
@Digits | 数字类型(原子和包装) | 验证数字的整数位和小数位的位数是否超过指定的长度@Digits(integer =2, fraction =2)privatedouble money;
|
YES |
String | 该字段为Email格式,才能通过 | YES | |
@Future | 时期、时间 | 验证日期是否在当前时间之后,否则无法通过校验@FutureprivateDate date;
|
YES |
@FutureOrPresent | 时期、时间 | 时间在当前时间之后 或者等于此时 | YES |
@Max | 数字类型(原子和包装) | //该字段的最大值为18,否则无法通过验证@Max(value=18)privateInteger age;
|
YES |
@Min | 数字类型(原子和包装) | 同上,不能低于某个值否则无法通过验证 | YES |
@Negative | 数字<0 | YES | |
@NegativeOrZero | 数字=<0 | YES | |
@NotBlank | String 该注解用来判断字符串或者字符,只用在String上面 | 字符串不能为null,字符串trim()后也不能等于“” |
NO |
@NotEmpty | String、集合、数组、Map、链表List | 不能为null,不能是空字符,集合、数组、map等size()不能为0;字符串trim()后可以等于“” |
NO |
@NotNull | 任何类型 | 使用该注解的字段的值不能为null,否则验证无法通过 | NO |
@Null | 修饰的字段在验证时必须是null,否则验证无法通过 | YES | |
@Past | 时间、日期 | 验证日期是否在当前时间之前,否则无法通过校验,必须是一个过去的时间或日期 | YES |
@PastOrPresent | 时间、日期 | 验证日期是否在当前时间之前或等于当前时间 | YES |
@Pattern | 用于验证字段是否与给定的正则相匹配@Pattern(regexp ="正则")privateString name;
|
YES | |
@Positive | 数字>0 | YES | |
@PositiveOrZero | 数字>=0 | YES | |
@Size | 字符串String、集合Set、数组Array、Map,List |
修饰的字段长度不能超过5或者低于1@Size(min =1, max =5)privateString name; 集合、数组、map等的size()值必须在指定范围内 //只能一个@Size(min =1, max =1)privateList<String> names;
|
YES |