使用SpringMVC时配合hibernate-validate进行参数的合法性校验,验证客户输入的数据是否合法,能节省一定的代码量。
1. 搭建Web工程并引入hibernate-validate依赖
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-cdi</artifactId>
<version>6.1.2.Final</version>
</dependency>
2. SpringMVC.xml配置
<!-- 将校验器交给适配器 -->
<mvc:annotation-driven validator="validator"></mvc:annotation-driven>
<!-- 配置校验器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 提供校验的类 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
<!-- 可以配置国际化错误信息保存的文件,也可在校验注解使用message属性进行配置但会失去国际化的功能 -->
<property name="validationMessageSource" ref="messageSource"></property>
</bean>
<!-- 读取外部配置文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 文件路径 -->
<property name="basenames">
<array>
<value>classpath:CustomValidationMessages</value>
</array>
</property>
<!-- 设置编码格式 -->
<property name="defaultEncoding" value="utf-8"></property>
<!-- 文件缓存的时间 :以秒为单位 -->
<property name="cacheSeconds" value="60"></property>
</bean>
自定义校验信息提示文件CustomValidationMessages.properties
emp.empno=\u96C7\u5458\u7F16\u53F7\u53EA\u80FD\u4E3A\u56DB\u4F4D\u6570\u5B57\u7EC4\u6210 //雇员编号只能为四位数字组成
emp.ename=\u96C7\u5458\u59D3\u540D\u53EA\u80FD\u7531\u6570\u5B57\u3001\u5B57\u6BCD\u3001\u4E0B\u5212\u7EBF\u3001\u957F\u77ED\u5FC5\u987B\u57286-10\u4F4D\u4E4B\u95F4 //雇员编号只能为四位数字组成
emp.job=\u804C\u4F4D\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A //职位名称不能为空
emp.email=\u975E\u6CD5\u90AE\u7BB1 //非法邮箱
3. 使用校验注解标注在属性上(DTO)
*每个注解都有message属性,该属性用于填写校验失败时的异常描述信息,当校验失败时可以获取对应的message属性值。
4. Spring MVC 数据校验
- Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。
- Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在 Spring MVC 中,可直接通过注解驱动的方式进行数据校验
- Spring 的 LocalValidatorFactroyBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean 中。
- Spring 本身并没有提供 JSR303 的实现,所以必须将 JSR303 的实现者的 jar 包放到类路径下。
- <mvc:annotation-driven/> 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @valid 注解即可让 Spring MVC 在完成数据绑定后执行数据校验的工作
- Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是 BindingResult 或 Errors 类型,这两个类都位于 org.springframework.validation 包中
- 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参
- Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或 getFieldErrors(String field)
-
BindingResult 扩展了 Errors 接口
5. 使用@Validated/@Valid注解开启对参数的校验
- @Validated注解表示开启Spring的校验机制,支持分组校验,声明在入参上。
- @Valid注解表示开启Hibernate的校验机制,不支持分组校验,声明在入参上。
- 在DTO后面紧跟BindingResult对象,那么当参数不符合时,能通过该对象直接获取不符合校验的message描述信息。
- 若使用了@Validated/@Valid注解开启校验,但DTO后面没有紧跟BindingResult对象,那么当参数不符合时,将直接返回400 Bad Request状态码。
6. 在页面上显示错误(需要引入SpringMVC表单标签库)
- Spring MVC 除了会将表单/命令对象的校验结果保存到对应的 BindingResult 或 Errors 对象中外,还会将所有校验结果保存到 “隐含模型”
- 即使处理方法的签名中没有对应于表单/命令对象的结果入参,校验结果也会保存在 “隐含对象” 中。
- 隐含模型中的所有数据最终将通过 HttpServletRequest 的属性列表暴露给 JSP 视图对象,因此在 JSP 中可以获取错误信息
- 在 JSP 页面上可通过 <form:errors path=“userName”> 显示错误消息
7. 示例
public class Emp {
@Max(value = 9999L)
@Min(value = 1000)
private int empno;
@Pattern(regexp = "[0-9A-Za-z_]{6,10}", message = "雇员姓名只能由数字、字母、下划线、长短必须在6-10位之间")
private String ename;
@NotBlank(message = "工作不能为空")
private String job;
private int mgr;
private Date hiredate;
@Email(message = "非法邮箱!")
private String email;
private double sal;
private double comm;
private Dept dept;
...
@RequestMapping("/adduser")
public void addUser(@Validated Emp emp, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<ObjectError> errors = bindingResult.getAllErrors();
for (ObjectError objectError : errors) {
System.out.println(objectError.getDefaultMessage());
}
}
System.out.println(emp.getHiredate());
}
8. 提示消息的国际化
上述例子代码中,我们自定义错误消息,使用了message 属性来自定义消息描述,但是这样做我们就失去了国际化的功能,消息国际化我们可以通过编写国际化配置文件来实现
当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看 WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。
配置校验器以及国际化配置文件
<!-- 将校验器交给适配器 -->
<mvc:annotation-driven validator="validator"></mvc:annotation-driven>
<!-- 配置校验器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 提供校验的类 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
<!-- 可以配置国际化错误信息保存的文件,也可在校验注解使用message属性进行配置但会失去国际化的功能 -->
<property name="validationMessageSource" ref="messageSource"></property>
</bean>
<!-- 读取外部配置文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 文件路径 -->
<property name="basenames">
<array>
<value>classpath:CustomValidationMessages</value>
</array>
</property>
<!-- 设置编码格式 -->
<property name="defaultEncoding" value="utf-8"></property>
<!-- 文件缓存的时间 :以秒为单位 -->
<property name="cacheSeconds" value="60"></property>
</bean>
自定义校验信息提示文件CustomValidationMessages.properties
emp.empno=\u96C7\u5458\u7F16\u53F7\u53EA\u80FD\u4E3A\u56DB\u4F4D\u6570\u5B57\u7EC4\u6210 //雇员编号只能为四位数字组成
emp.ename=\u96C7\u5458\u59D3\u540D\u53EA\u80FD\u7531\u6570\u5B57\u3001\u5B57\u6BCD\u3001\u4E0B\u5212\u7EBF\u3001\u957F\u77ED\u5FC5\u987B\u57286-10\u4F4D\u4E4B\u95F4 //雇员编号只能为四位数字组成
emp.job=\u804C\u4F4D\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A //职位名称不能为空
emp.email=\u975E\u6CD5\u90AE\u7BB1 //非法邮箱
校验目标类
public class Emp {
@Max(value = 9999L)
@Min(value = 1000)
private int empno;
@Pattern(regexp = "[0-9A-Za-z_]{6,10}", message = "{emp.ename}")
private String ename;
@NotBlank(message = "{emp.job}")
private String job;
private int mgr;
private Date hiredate;
@Email(message = "{emp.email}")
private String email;
private double sal;
private double comm;
private Dept dept;// 一对一
...
9.分组校验
9.1. 什么是分组校验?
校验规则是在pojo 制定的,而同一个pojo可以被多个Controller使用,此时会有问题,即:不同的Controller方法对同一个pojo进行校验,此时这些校验信息是共享在这不同的Controller方法中,但是实际上每个Controller方法可能需要不同的校验,在这种情况下,就需要使用分组校验来解决这种问题。
通俗的讲,一个pojo中有很多属性,controller中的方法1可能只需要校验pojo中的属性1,controller中的方法2只需要校验pojo中的属性2,但是pojo中的校验注解有很多,怎样才能使方法1只校验属性1,方法二只校验属性2呢?就需要用分组校验来解决了。
9.2. 定义分组
就是定义空的接口,接口类只作为这个分组标识来使用,看下面的用法,就知道其意义何在了
9.3. 使用分组
9.4. controller方法
在这个方法中,那么就只会校验Emp这个pojo中有Group1.class这个分组的校验注解,而不会在校验其他的
10 扩展知识
使用SpringMVC表单标签库来参数校验https://www.cnblogs.com/hemou/p/12356175.html
SpringMVC表单标签库https://www.jianshu.com/p/b1f2d50b671c
SpringMVC_数据校验https://blog.csdn.net/qq_40794973/article/details/97967016