SpringMVC数据验证(AOP处理Errors和方法验证)
什么是JSR303?
JSR 303 – Bean Validation 是一个数据验证的规范,2009 年 11 月确定最终方案。
Hibernate Validator 是 Bean Validation 的最佳实践。
为什么使用JSR,松耦合,让业务代码的职责更加清晰。
松耦合就是职责更加清晰,每个人都有自己的职责,如果你的代码进行改动,我不用改动或者仅仅少量改动就可以发布和部署。
准备工作
maven 配置
<!-- JSR 303 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<!-- Hibernate validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.0.Final</version>
</dependency>
SpringMVC 配置
<mvc:annotation-driven validator="validator">
</mvc:annotation-driven>
<!-- 配置校验器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 校验器,使用Hibernate校验器 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下面的ValidationMessages.properties文件, -->
<property name="validationMessageSource" ref="messageSource"/>
</bean>
<!-- 校验错误信息配置文件,也可以不配置,直接使用注解中的message即可 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 资源文件名 -->
<property name="basenames">
<list>
<value>classpath:messageSource</value>
</list>
</property>
<!-- 资源文件编码格式 -->
<property name="fileEncodings" value="utf-8"/>
<!-- 对资源文件内容缓存时间,单位秒 -->
<property name="cacheSeconds" value="120"/>
</bean>
常用校验注解
| 注解 | 运行时检查 |
|---|---|
| @AssertFalse | 被注解的元素必须为false |
| @AssertTrue | 被注解的元素必须为true |
| @DecimalMax(value) | 被注解的元素必须为一个数字,其值必须小于等于指定的最大值 |
| @DecimalMin(Value) | 被注解的元素必须为一个数字,其值必须大于等于指定的最小值 |
| @Digits(integer=, fraction=) | 被注解的元素必须为一个数字,其值必须在可接受的范围内 |
| @Future | 被注解的元素必须是日期,检查给定的日期是否比现在晚 |
| @Max(value) | 被注解的元素必须为一个数字,其值必须小于等于指定的最大值 |
| @Min(value) | 被注解的元素必须为一个数字,其值必须大于等于指定的最小值 |
| @NotNull | 被注解的元素必须不为null |
| @Null | 被注解的元素必须为null |
| @Past(java.util.Date/Calendar) | 被注解的元素必须过去的日期,检查标注对象中的值表示的日期比当前早 |
| @Pattern(regex=, flag=) | 被注解的元素必须符合正则表达式,检查该字符串是否能够在match指定的情况下被regex定义的正则表达式匹配 |
| @Size(min=, max=) | 被注解的元素必须在制定的范围(数据类型:String, Collection, Map and arrays) |
| @Valid | 递归的对关联对象进行校验, 如果关联对象是个集合或者数组, 那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验 |
| @CreditCardNumber | 对信用卡号进行一个大致的验证 |
| 被注释的元素必须是电子邮箱地址 | |
| @Length(min=, max=) | 被注解的对象必须是字符串的大小必须在制定的范围内 |
| @NotBlank | 被注解的对象必须为字符串,不能为空,检查时会将空格忽略 |
| @NotEmpty | 被注释的对象必须不为空(数据:String,Collection,Map,arrays) |
| @Range(min=, max=) | 被注释的元素必须在合适的范围内 (数据:BigDecimal, BigInteger, String, byte, short, int, long and 原始类型的包装类 ) |
| @URL(protocol=, host=, port=, regexp=, flags=) | 被注解的对象必须是字符串,检查是否是一个有效的URL,如果提供了protocol,host等,则该URL还需满足提供的条件 |
Bean验证
首先向我们的bean中添加注解。
public class User {
@NotEmpty(message = "用户名不为空")
private String username;
@NotEmpty(message = "密码不为空")
private String password;
// getter 和 setter
}
Controller中配置:
@RestController
public class UserAction {
@PostMapping("/login")
public Result login(@Validated User user, Errors errors) {
System.out.println(user);
if (errors.hasErrors()) {
return ResultUtil.messageResult(errors);
}
return ResultUtil.SUCCESS_RESULT;
}
}
我们只需要在要校验的bean前面添加@Validated,在需要校验的bean后面添加Errors对象来接收校验出错信息即可,然后根据错误信息进行判断和返回错误信息给前端。
注意:
@Validated 和 Errors errors 是成对出现的,并且形参顺序是固定的(一前一后)。也就是所每一个@Validated后面必须跟一个Errors,需要验证多个bean,后面就跟多个Errors。
AOP处理Errors
如果我们通过JSR来验证bean对象,那么在每个需要验证的方法中都需要处理Error对象,很容易想到可以通过AOP的方式来统一处理错误对象,并且组织错误信息,返回给前端。
通过一个环绕通知对所有的action方法尽心拦截,如果发现有Errors对象存在,就获取所有的错误信息,封装为一个list返回前端。
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;
import java.util.ArrayList;
import java.util.List;
/**
* @author 李文浩
* @version 2017/10/8.
*/
public class ValidationAdvice {
/**
* 切点处理
*
* @param pjp
* @return
* @throws Throwable
*/
public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
Errors errors = null;
Object[] args = pjp.getArgs();
if (null != args && args.length != 0) {
for (Object object : args) {
if (object instanceof BindingResult) {
errors = (BindingResult) object;
break;
}
}
}
if (errors != null && errors.hasErrors()) {
List<ObjectError> allErrors = errors.getAllErrors();
List<String> messages = new ArrayList<String>();
for (ObjectError error : allErrors) {
messages.add(error.getDefaultMessage());
}
return ResultUtil.messageResult(messages);
}
return pjp.proceed();
}
}
Spring配置:
<bean id="validationAdvice" class="studio.jikewang.util.ValidationAdvice" />
<aop:config>
<aop:pointcut id="validation1" expression="execution(public * studio.jikewang.action.*.*(..))" />
<aop:aspect id="validationAspect" ref="validationAdvice">
<aop:around method="aroundMethod" pointcut-ref="validation1" />
</aop:aspect>
</aop:config>
@Validated 和 @Valid
@Valid是使用Hibernate Validation的时候使用。
Java的JSR303声明了这类接口,然后hibernate-validator对其进行了实现。@Validated是只用Spring Validator校验机制使用。
方法参数验证
Spring提供了MethodValidationPostProcessor类,用于对方法的校验。
Controller中配置:
@RestController
@Validated
public class UserAction {
@PostMapping("/login")
public Result login(@NotEmpty(message = "用户名不为空") String username,
@NotEmpty(message = "密码不为空") String password) {
return ResultUtil.SUCCESS_RESULT;
}
}
xml配置(最好是配置在<mvc:annotation-driven validator="validator" />上面,不然会有未知错误)如下:
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
</bean>
在校验遇到非法的参数时会抛出ConstraintViolationException,可以通过getConstraintViolations获得所有没有通过的校验ConstraintViolation集合,可以通过它们来获得对应的消息。
我们同样使用 @ExceptionHandler 捕捉ConstraintViolationException异常处理全局异常信息。
然后将所有的错误信息封装好返回给前端。
@RestControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(ConstraintViolationException.class)
public Result handleConstraintViolationException(ConstraintViolationException e) {
List<String> list = new ArrayList<String>();
for (ConstraintViolation<?> s : e.getConstraintViolations()) {
System.out.println(s.getInvalidValue() + ": " + s.getMessage());
list.add(s.getMessage());
}
Result result = new Result();
result.setStatus("0");
result.setMessage(list);
return result;
}
}
使用@Validated验证list
现在我遇到一个新的需求,我需要前端给我传递一个对象数组,于是我使用一个list去接收,但是无法获得验证信息。
于是将list重新包装一下。
public class ValidList<E> {
@Valid
private List<E> list;
public List<E> getList() {
return list;
}
public void setList(List<E> list) {
this.list = list;
}
}
Controller中配置:
@RestController
public class UserAction {
@PostMapping("/login")
public Result login(@Validated ValidList<User> users, Errors errors) {
System.out.println(users.getList());
if (errors.hasErrors()) {
return ResultUtil.messageResult(errors);
}
return ResultUtil.SUCCESS_RESULT;
}
}
然后为了只返回第一个验证失败的信息(如果不更改,就会将所有的出错信息返回给前端),更改ValidationAdvice如下:
public class ValidationAdvice {
/**
* 切点处理
*
* @param pjp
* @return
* @throws Throwable
*/
public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
boolean isValidList = false;
Errors errors = null;
if (null != args && args.length != 0) {
for (Object object : args) {
if (object instanceof ValidList) {
isValidList = true;
}
if (object instanceof BindingResult) {
errors = (BindingResult) object;
break;
}
}
}
if (errors != null && errors.hasErrors()) {
List<ObjectError> allErrors = errors.getAllErrors();
List<String> messages = new ArrayList<String>();
for (ObjectError error : allErrors) {
if (isValidList) {
messages.add(error.getDefaultMessage());
break;
} else {
messages.add(error.getDefaultMessage());
}
}
return ResultUtil.messageResult(messages);
}
return pjp.proceed();
}
}
这样即可验证list。
总结
AOP的思想是贯穿我们的开发的,使用AOP的思想可以大大提高我们的开发效率,减少重复代码。
参考文档:
SpringMVC数据验证(AOP处理Errors和方法验证)的更多相关文章
- SpringMVC数据验证
SpringMVC数据验证——第七章 注解式控制器的数据验证.类型转换及格式化——跟着开涛学SpringMVC 资源来自:http://jinnianshilongnian.iteye.com/blo ...
- 表单验证插件 jquery.validata 使用方法
参考资料:http://www.runoob.com/jquery/jquery-plugin-validate.html 下载地址 jquery.validate插件的文档地址http://docs ...
- SpringMVC——数据校验
数据校验在web应用里是非常重要的功能,尤其是在表单输入中.在这里采用Hibernate-Validator进行校验,该方法实现了JSR-303验证框架支持注解风格的验证. 一.导入jar包 若要实现 ...
- SpringMVC数据校验并通过国际化显示错误信息
目录 SpringMVC数据校验并通过国际化显示错误信息 SpringMVC数据校验 在页面中显示错误信息 通过国际化显示错误信息 SpringMVC数据校验并通过国际化显示错误信息 SpringMV ...
- 表单验证之validform.js使用方法
一.validform有什么用? 网页上有大量的input需要你进行验证的时候,如果是弹窗的话,需要不停地判断,如果为空,弹窗.如果不是数字,弹窗. 所以要将这么多验证交给一个js去验证. 二.我现在 ...
- SpringMVC常用注解,返回方式,路径匹配形式,验证
常用注解元素 @Controller 标注在Bean的类定义处 @RequestMapping 真正让Bean具备 Spring MVC Controller 功能的是 @RequestMapping ...
- 【springmvc Request】 springmvc请求接收参数的几种方法
通过@PathVariabl注解获取路径中传递参数 转载请注明出处:springmvc请求接收参数的几种方法 代码下载地址:http://www.zuida@ima@com/share/1751862 ...
- spring aop获取目标对象的方法对象(包括方法上的注解)
这两天在学习权限控制模块.以前看过传智播客黎活明老师的巴巴运动网视频教程,里面就讲到权限控制的解决方案,当时也只是看看视频,没有动手实践,虽说看过几遍,可是对于系统中的权限控制还是很迷茫,所以借着这次 ...
- 《Java从入门到放弃》入门篇:springMVC数据传递
springMVC中的数据传递方式与JSP和Struts2相比,更加的简单.具体有什么样的区别呢?我们通过下面这张图来对比就知道了. 随手画的,有些错别字,不用太在意..... 接下来,进入正题,sp ...
随机推荐
- for循环相关的一个问题
首先,我在for循环外定义了两个变量. //在4条直线方向,获取预置点两端的8个点的棋盘信息 ,r=,c=;//step代表直线,r,c分别代表加上偏移量之后的坐标 ,myCount=,hisCoun ...
- C#并发编程实例讲解-概述(01)
在工作中经常遇到需要并发编程的实例,一直没有时间来整理,现在空了下来,个人整理对并发一下理解. 关于并发编程的几个误解 误解一:并发编程就是多线程 实际上多线只是并发编程的一中形式,在C#中还有很多更 ...
- cs231n spring 2017 lecture15 Efficient Methods and Hardware for Deep Learning 听课笔记
1. 深度学习面临的问题: 1)模型越来越大,很难在移动端部署,也很难网络更新. 2)训练时间越来越长,限制了研究人员的产量. 3)耗能太多,硬件成本昂贵. 解决的方法:联合设计算法和硬件. 计算硬件 ...
- [51nod][cf468D]1558 树中的配对
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1558 不是很懂dalao们用线段树是怎么写的…… 反正找出重心以后每个子 ...
- 哈密顿绕行世界问题(dfs+记录路径)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2181 哈密顿绕行世界问题 Time Limit: 3000/1000 MS (Java/Others) ...
- Redis进阶实践之四Redis的基本数据类型
一.引言 今天正式开始了Redis的学习,如果要想学好Redis,必须先学好Redis的数据类型.Redis为什么会比以前的Memchaed等内存缓存软件使用的更频繁,适用范围更广呢?就是因为R ...
- angularJS 与angujs-sku实现购物车组合查询
原网址:http://sentsin.com/web/1069.html demo : https://codepen.io/hzxs1990225/pen/VYyOdW 修复版文件下载:htt ...
- [国嵌笔记][036][关闭MMU和CACHE]
关闭MMU和CACHE 1.Cache是一种容量小,但存取速度非常快的存储器,它保存最近用到的存储器中数据的拷贝.按功能分为ICache(指令Cache)和DCache(数据Cache) 2.虚拟地址 ...
- 复选框之checked属性
今天无意中看到同事在学习复选框里面的checked属性的应用,当时看了一下,感觉熟悉而又陌生,发现checked属性其实还是挺奇怪的,感觉这里很有必要做一下笔记: 1.html中的checked属性. ...
- thinkphp操作完提示信息该怎么弄成弹出层啊?
http://www.thinkphp.cn/topic/21929.html 浏览:11879 发布日期:2014/08/22 分类:求助交流 关键字: thinkphp success跳转 弹出层 ...