SpringBoot支持JSR303的验证规范。
JSR303 规范(Bean Validation 规范)为 JavaBean 验证定义了相应的元数据模型和 API。在应用程序中,通过使用 Bean Validation 或是你自己定义的 constraint,例如 @NotNull, @Max, @ZipCode , 就可以确保数据模型(JavaBean)的正确性。constraint 可以附加到字段,getter 方法,类或者接口上面。对于一些特定的需求,用户可以很容易的开发定制化的 constraint。Bean Validation 是一个运行时的数据验证框架,在验证之后验证的错误信息会被马上返回。
JSR303验证主要是由一系列的注解所组成,这些注解可在导入了hibernate-validator
后直接使用。
No. | 注解 | 说明 |
---|---|---|
1 | @Null | 验证对象是否为null |
2 | @NotNull | 验证对象是否不为null, 无法查检长度为0的字符串 |
3 | @NotBlank | 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格. |
4 | @NotEmpty | 检查约束元素是否为NULL或者是EMPTY. |
5 | @AssertTrue | 验证 Boolean 对象是否为 true |
5 | @AssertFalse | 验证 Boolean 对象是否为 false |
7 | @Size(min=, max=) | 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内 |
8 | @Length(min=, max=) | 验证字符串长度是否在给定范围内 |
9 | @Past | 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期 |
10 | @Future | 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期 |
11 | @Pattern | 验证 String 对象是否符合正则表达式的规则,regexp:正则表达式 |
12 | @Min | 验证 Number 和 String 对象是否大等于指定的值 |
13 | @Max | 验证 Number 和 String 对象是否小等于指定的值 |
14 | @DecimalMax | 被标注的值必须不大于约束中指定的最大值 |
15 | @DecimalMin | 被标注的值必须不小于约束中指定的最小值 |
16 | @Digits | 验证 Number 和 String 的构成是否合法 |
17 | @Digits(integer=,fraction=) | 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。 |
7 | @Range(min=, max=) | 被指定的元素必须在合适的范围内 |
18 | @Range(min=,max=) | 验证Number是否在给定范围内 |
19 | @Valid | 递归的对关联对象进行校验, |
20 | @CreditCardNumber | 信用卡验证 |
21 | 验证是否是邮件地址,如果为null,不进行验证,算通过验证。 | |
22 | @URL | 验证是否为url |
JSR303验证流程如下所示:
一、使用JSR303校验参数
使用JSR303校验需要在接收的VO类上添加相关的验证注解,随后在接收参数上配置@Valid
注解。
JSR303验证由Hibernate提供,所以需要在项目中添加maven依赖:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version>
</dependency>
编写参数接收类,并配置校验注解:
@Data
public class MessageParams {
@Range(min = 0, max = 100)
private Integer id;
@NotNull
private String title;
@NotNull
private String content;
}
新建MessageAction接收参数MessageParams:
@RestController
@RequestMapping("/message/*")
public class MessageAction {
@RequestMapping("/echo")
public Object echo(@Valid MessageParams params) {
return params;
}
}
访问:http://localhost:8080/message/echo发现系统出现了由于验证导致的异常。
二、以Rest形式返回错误信息
对于前后端分离的架构来说,直接返回异常信息显然是不可取的,错误信息应以JSON形式返回。
编写返回数据公共格式类:
public class ResultMessage<T> {
private Integer code; // 返回编码
private String message; // 信息
private T result; // 返回结果
private boolean success;
public ResultMessage() {}
public ResultMessage(Integer code, String message, T result, boolean success) {
this.code = code;
this.message = message;
this.result = result;
this.success = success;
}
// setter,getter略
}
开启全局异常配置:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResultMessage<?> exceptionHandler(HttpServletRequest request, Exception e) {
Map<String, String> resultMap = new LinkedHashMap<>();
resultMap.put("uri",request.getRequestURI());
resultMap.put("exception",e.getClass().getName());
return new ResultMessage(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), resultMap, false);
}
}
重新启动项目,再次访问:http://localhost:8080/message/echo,此时的错误信息会以JSON形式返回。
三、校验普通参数
如果Controller层方法接受的参数不是VO类型,而是Integer、String这样的基本数据类型,那么校验规则需要写在接收参数上,并且需要在Controller类上配置@Validated
注解。
@RestController
@RequestMapping("/validate/*")
@Validated // 启用JSR303校验
public class ValidateAction {
@RequestMapping("/get")
public String get(@NotNull @Length(min = 5, max = 10) String data) {
return data;
}
}
访问:http://localhost:8080/validate/get发现NotNull
注解配置生效。
四、设置错误信息
进入@NotNull
注解源代码发现,其内部有个字段message配置的是验证出错的默认提示信息(实际上错误信息被配置在资源文件中,此处显示的仅是资源的key值),因此可以通过修改message的内容自定义错误提示信息。
修改MessageParams
类,添加校验提示信息:
@Data
public class MessageParams {
@Range(min = 0, max = 100, message = "id应在0-100之间")
private Integer id;
@NotNull(message = "标题不能为空")
private String title;
@NotNull(message = "内容不能为空")
private String content;
}
重新启动项目,访问:http://localhost:8080/message/echo,此时显示的是自定义的提示信息。
此时虽然完成了错误信息的自定义显示,但是直接写在注解中不方便后期的修改,一般的做法是写在配置文件中,在resource路径下新建ValidationMessages.properties文件(必须叫这个文件名)。
message.id.range.error=id应在0-100之间
message.title.notnull.error=标题不能为空
message.content.notnull.error=内容不能为空
将MessageParams中JSR303注解的message字段全部改为表达式的形式,引用ValidationMessages.properties中的key。
@Data
public class MessageParams {
@Range(min = 0, max = 100, message = "{message.id.range.error}")
private Integer id;
@NotNull(message = "{message.title.notnull.error}")
private String title;
@NotNull(message = "{message.content.notnull.error}")
private String content;
}
重新启动项目,访问:http://localhost:8080/message/echo得到同样的结果。
五、自定义验证器
仿写@NotNull
注解,实现自定义正则校验注解。
新建RegexValidator
注解:
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Target({FIELD, PARAMETER}) // 该注解允许使用在成员和参数上
@Retention(RUNTIME) // 运行时生效
@Constraint(validatedBy = RegexConstraintValidate.class) // 绑定正则处理类
public @interface RegexValidator { // 自定义正则注解
String message() default "正则验证出错"; // 错误信息
Class<?>[] groups() default {}; // 验证分组
Class<? extends Payload>[] payload() default {}; // 附加数据源信息
String pattern(); // 接受验证正则
}
新建RegexValidator注解的处理类并实现ConstraintValidator接口:
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class RegexConstraintValidate implements ConstraintValidator<RegexValidator, Object> {
private String regexExpression;
@Override
public void initialize(RegexValidator constraintAnnotation) {
regexExpression = constraintAnnotation.pattern(); // 通过注解内容获取正则表达式
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if(value==null) { // 数据为null,验证失败
return false;
}
return value.toString().matches(regexExpression); // 返回验证结果
}
}
修改MessageParams
类,追加date字段并配置正则验证。
@Data
public class MessageParams {
@Range(min = 0, max = 100, message = "{message.id.range.error}")
private Integer id;
@NotNull(message = "{message.title.notnull.error}")
private String title;
@NotNull(message = "{message.content.notnull.error}")
private String content;
@RegexValidator(pattern = "\\d{4}\\-\\d{2}\\-\\d{2}")
private String date;
}
启动项目,访问:http://localhost:8080/message/echo?id=8&title=特大新闻&content=xxx&date=123456