开发web项目有时候我们需要对controller层传过来的参数进行一些基本的校验,比如非空,非null,整数值的范围,字符串的个数,日期,邮箱等等。最常见的就是我们直接写代码校验,这样以后比较繁琐,而且不够灵活。
前段时间提交代码审核,同事提了一个代码规范缺陷:参数校验应该放在controller层。到底应该如何做参数校验呢?
service负责数据的读写以及根据各种条件读写,action确保数据正确的读写。action就相当于一个窗口,它就应该筛选出满足条件的和过滤掉不满足条件的,属于基本校验。
service就相当于一个服务者,为action分派的任务进行处理,比如service接收到一个用户id,但是这个用户不存在,属于业务校验,可以在service层抛出。
Bean Validation 1.0(JSR-303)是一个校验规范,在spring Boot项目由于自带了hibernate validator 5(http://hibernate.org/validator/)实现,所以我们可以非常方便的使用这个特性 。
使用Hibernate Validate
引入依赖
org.hibernate
hibernate-validator
4.3.1.Final
注解 | 说明 |
---|---|
@Length(min=,max=) | 检查所属的字段的长度是否在min和max之间,只能用于字符串 |
@Range(min=,max=,message=) | 被注释的元素必须在合适的范围内 |
@Max | 该字段的值只能小于或等于该值 |
@Min | 该字段的值只能大于或等于该值 |
@NotNull | 不能为null |
@NotBlank 不能为空, | 检查时会将空格忽略 |
@NotEmpty 不能为空, | 这里的空是指空字符串 |
@Pattern(regex=,flag=) | 被注释的元素必须符合指定的正则表达式 |
使用姿势需要搭配在Controller中搭配@Validated或@Valid注解一起使用,@Validated和@Valid注解区别不是很大,一般情况下任选一个即可,区别如下:
注解 | @Validated | @Valid |
---|---|---|
所属的包 | 属于org.springframework.validation.annotation包下的,是spring提供的 | 属于javax.validation包下,是jdk给提供的 |
是否支持分组和排序 | 是 | 否 |
虽然@Validated比@Valid更加强大,在@Valid之上提供了分组功能和验证排序功能,不过在实际项目中一直没有用到过 Hibernate-validate框架中的注解是需要加在实体中一起使用的
定义一个实体
public class DataSetSaveVO {
//唯一标识符为空
@NotBlank(message = user uuid is empty)
//用户名称只能是字母和数字
@Pattern(regexp = ^[a-z0-9]+$, message = user names can only be alphabetic and numeric)
@Length(max = 48, message = user uuid length over 48 byte)
private String userUuid;
//数据集名称只能是字母和数字
@Pattern(regexp = ^[A-Za-z0-9]+$, message = data set names can only be letters and Numbers)
//文件名称过长
@Length(max = 48, message = file name too long)
//文件名称为空
@NotBlank(message = file name is empty)
private String name;
//数据集描述最多为256字节
@Length(max = 256, message = data set description length over 256 byte)
//数据集描述为空
@NotBlank(message = data set description is null)
private String description;
}
说明:message字段为不符合校验规则时抛出的异常信息
Controller层中的方法
@PostMapping
public ResponseVO createDataSet(@Valid @RequestBody DataSetSaveVO dataSetVO) {
return ResponseUtil.success(dataSetService.saveDataSet(dataSetVO));
}
说明:在校验的实体DataSetSaveVO旁边添加@Valid或@Validated注解
补充:使用自定义参数注解
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdentityCardNumberValidator.class)
public @interface IdentityCardNumber {
String message() default "身份证号码不合法";
Class[] groups() default {};
Class[] payload() default {};
}
这个注解是作用在Field字段上,运行时生效,触发的是IdentityCardNumber这个验证类。
message 定制化的提示信息,主要是从ValidationMessages.properties里提取,也可以依据实际情况进行定制
groups 这里主要进行将validator进行分类,不同的类group中会执行不同的validator操作
payload 主要是针对bean的,使用不多。
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class IdentityCardNumberValidator implements ConstraintValidator {
@Override
public void initialize(IdentityCardNumber identityCardNumber) {
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
return IdCardValidatorUtils.isValidate18Idcard(o.toString());
}
}
校验工具类IdCardValidatorUtils.class
@NotBlank(message = "身份证号不能为空")
@IdentityCardNumber(message = "身份证信息有误,请核对后提交")
private String clientCardNo;