你未看此花时,则此花与汝心同归于寂, 你来看此花时,此花颜色一时明白起来,便知此花不在你的心外。
上一章简单介绍了SpringMVC的国际化操作(十),如果没有看过,请观看上一章。
一. SpringMVC 的数据验证
数据验证,包括 正常用户的误操作,和恶意用户的恶意操作两种。 验证,可以分为前端验证和后端验证。 前端验证主要是通过 JS来进行的,不安全。 后端验证,是通过服务器后端代码进行验证的。
SpringMVC 的验证,可以采用两种形式。
1 . SpringMVC 提供的validation校验框架 进行验证, 实现 validator 接口。
2 . 利用 JSR 303 (Java 验证规范) 来进行验证数据。 采用在Bean 属性上面添加注解的形式。
通常采用 JSR 303 的形式进行验证。
二. validation 校验框架进行验证
二.一 校验框架重要的接口和类
SpringMVC 在validation 校验框架中,提供了几个重要的接口 和类。 在 spring-context-4.2.4.RELEASE.jar 里面。
1 . org.springframework.validation.Validator 接口
package org.springframework.validation;
public abstract interface Validator
{
public abstract boolean supports(Class<?> paramClass);
public abstract void validate(Object paramObject, Errors paramErrors);
}
有两个方法, supports() 验证能否对类 paramClass 进行校验。
validate() 方法,对目标类 paramObject 进行校验,校验后的数据存放到 paramErrors 中。
2 . org.springframework.validation.Errors 接口
用来存放错误信息的接口。 通过 reject()或者是rejectValue() 方法来进行添加错误信息。
有一个 BindingResult 接口扩展了该接口。 全称是: org.springframework.validation.BindingResult
3 . org.springframework.validation.ValidationUtils
提供了很多验证方法, 是一个验证字段属性是否存在错误的工具类。
主要有两个方法, rejectIfEmpty() 验证是否为空。 rejectIfEmptyOrWhitespace() 验证是否为空或者是空白字符串。
4 . org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
将其定义到需要数据验证的Bean 中。 既实现了Spring 的validator 接口,也实现了 JSR303 的Validator 接口。
默认装载了 LocalValidatorFactoryBean,所以不需要手动显式实现。
二.二 Validator 验证数据的小例子(一)
这个,采用 @InitBinder 的方式,可以参考 第八章讲解 form:errors 标签时的例子。 去例子查看
(因为这一块不怎么重要,就不重复写代码了。 留下内容主要讲 JSR303)
二.二 Validator 验证数据的小例子(二)
这个,采用 注解的方式,进行注入进去。
1 . 后端, 创建UserValidator 验证器。 对用户名,密码,和年龄进行验证。
采用 @Repository 注解。
package com.yjl.validator;
import org.springframework.stereotype.Repository;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.yjl.pojo.User;
@Repository(value="userValidator")
public class UserValidator implements Validator{
@Override
public boolean supports(Class<?> paramClass) {
return User.class.isAssignableFrom(paramClass);
}
@Override
public void validate(Object target, Errors errors) {
//1. 验证不为空
ValidationUtils.rejectIfEmpty(errors,"name",null,"用户名不能为空");
ValidationUtils.rejectIfEmpty(errors,"password",null,"密码不能为空");
//2 .类型转换,进行更深入的验证。
User user=(User)target;
if(user.getName()!=null&&!"".equals(user.getName())){
if(user.getName().length()>10){
errors.rejectValue("name",null,"用户名不能超过10个字符");
}
}
if(user.getPassword()!=null&&!"".equals(user.getPassword())){
if(user.getPassword().length()>15||
user.getPassword().length()<6){
errors.rejectValue("password",null,"密码长度在6~15之间");
}
}
if(user.getAge()!=null){
if(user.getAge()<18){
errors.rejectValue("age",null,"别装嫩噢");
}
}
}
}
2 .UserAction 处理,进行验证。
@Autowired
@Qualifier(value="userValidator")
private UserValidator userValidator;
//转到登录的页面
@RequestMapping(value="toLogin")
public String toLogin(Model model){
model.addAttribute("user",new User());
return "user/login";
}
//绑定到user对象。
@RequestMapping(value="login")
public String login(@ModelAttribute User user,Model model,Errors errors){
model.addAttribute("user",user);
//进行验证
userValidator.validate(user, errors);
if(errors.hasFieldErrors()){ //有错的话,就返回登录页面。
return "user/login";
}
return "user/list";
}
3 . springmvc.xml 配置文件中,要添加 com.yjl.validator 的扫描包。
<context:component-scan base-package="com.yjl.action,com.yjl.validator"></context:component-scan>
<mvc:annotation-driven></mvc:annotation-driven>
4 . 前端页面代码
<body>
<h2>两个蝴蝶飞,数据验证使用</h2>
<form:form commandName="user" action="login.action" method="post">
<form:label path="name">姓名:</form:label>
<form:input path="name"/>
<form:errors path="name"></form:errors> <br/>
<form:label path="password">密码:</form:label>
<form:input path="password"/> <!--为了能看见值,这里用 form:input,而不是form:password -->
<form:errors path="password"></form:errors> <br/>
<form:label path="age">年龄:</form:label>
<form:input path="age"/>
<form:errors path="age"></form:errors> <br/>
<form:button>提交</form:button>
</form:form>
</body>
5 . 重启服务器,进行相关的验证
对一些情况进行验证。
不输入用户名和密码时
密码长度不够时,用户名是正常的
用户名长度过多,年龄输入过小时,
这就是springmvc 的validation 验证框架的简单使用,功能很简单,所以需要看下面的JSR 303的验证。
三. JSR 303 验证。
JSR 303 的验证,主要是通过 注解的方式,在Bean 的属性上面进行添加注解,进行验证,直接作用在 Java Bean 上面,又叫做 Bean Validation .
官方参考的是Hibernate Validation. 所以,重点学习 Hibernate的验证, 而不是Apache 的验证。
需要导入相应的包:
三.一 JSR 303 验证注解
JSR 303 的注解,在 validation-api-1.1.0.Final.jar 包里面。 javax.validation.constraints
注解 |
功能 |
范例 |
---|---|---|
@Null |
验证对象为 null |
@Null private String description; |
@NotNull |
验证不为空,只验证基本数据, 不能验证长度为0的字符串 |
@NotNull private Integer age; |
@AssertTrue |
验证为 true |
@AssertTrue private boolean isMarriage; |
@AssertFalse |
验证为false |
@AssertFalse private String isMarriage; |
@Max(value) |
整数数字类型的,最大值为value |
@Max(120) private Integer age; |
@Min(value) |
整数数字类型的,最小值为value |
@Min(18) private Integer age; |
@DecimalMax(value) |
浮点型,最大值,value为字符串形式 |
@DecimalMax(value=“100.00”) private double score; |
@DecimalMin(value) |
浮点型,最小值,value为字符串形式 |
@DecimalMin(value=“100.00”) private double score; |
@Digits(integer,fraction) |
浮点型是否符合格式,integer为整数, fraction为小数 |
@Digits(integer=3,fraction=2) private double score; |
@Size(min,max) |
数组,List,Map,Set,String,的最大值和最小值 |
@Size(min=2,max=10) private List hobbys; |
@Past |
日期 Date 或者是日历 Calendar 对象 是否在当前时间 now 之前 |
@Past private Date birthday; |
@Future |
日期 Date 或者是日历 Calendar 对象 是否在当前时间 now 之后 |
@Future private Date invalid; |
@Pattern |
正则表达式验证,用regexp来表达式 |
@Pattern(regexp=“1[0~9]{10}”) private String phone; |
三.二 Hibernate 扩展的验证注解
Hibernate 除了支持 上面 的JSR 303的注解之外,还添加了一些新的注解。
在 hibernate-validator-5.2.4.Final.jar 包里面。 org.hibernate.validator.constraints
注解 |
功能 |
范例 |
---|---|---|
@NotBlank |
不为空,长度不能为0. 只能是字符串 会将字符串先trim()去除左右空格 |
|
@URL |
合法的url |
@URL private String mainUrl; |
@Email |
合法的Email 地址 |
@Email private String email; |
@CreditCardNumber |
合法的信用卡号 |
@CreditCardNumber private String card; |
@Length(min,max) |
字符串的长度在 min与max的范围之内 |
@Length(min=6,max=15) private String password; |
@NotEmpty |
判断是否为空或者是Empty, 用于Array,Map,Collection,String |
@NotEmpty private String name; |
@Range(min,max,message) |
数字的范围,min为最小值,max为最大值 message为提示信息 |
@Range(min=18,max=100,message=“年龄需要在18~100之间”) private Integer age; |
四. JSR 303 验证数据
四.一 引入jar包
四.二 在User.java 的各个属性上添加不同的约束注解
public class User implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;
@NotBlank(message="用户名不能为空12341")
@Length(max=10,message="用户名不能超过10个字")
private String name;
@NotBlank(message="密码不能为空")
@Length(min=6,max=15,message="密码长度应在6~15之间")
private String password;
@Range(min=18,max=100,message="年龄需要在18~100之间")
private Integer age;
@NotBlank(message="描述不能为空")
private String description;
@DecimalMin(value="0.00",message="成绩的最小值为0")
@DecimalMax(value="100.00",message="成绩的最大值不能超过100")
@NumberFormat(pattern="###,##")
private double score;
//定义一个日期类
@DateTimeFormat(pattern="yyyy-MM-dd")
@Past(message="填写的出生日期必须要在当前日期之前")
private Date birthday;
@Pattern(regexp="1[3456789]\\d{9}",message="请输入正确的电话号码")
private String phone;
@Email
private String email;
// 后面,就是setter和getter方法了。
}
四.三 springmvc.xml 的配置文件,将hibernate进行配置
<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"></property>
</bean>
四.四 后端,Action 进行处理
@Controller
@RequestMapping(value="/user")
public class UserAction {
//转到登录的页面
@RequestMapping(value="toLogin")
public String toLogin(Model model){
model.addAttribute("user",new User());
return "user/login";
}
//绑定到user对象。
@RequestMapping(value="login")
public String login(@Validated User user,
BindingResult bindingResult,Model model){
model.addAttribute("user",user);
if(bindingResult.hasErrors()){ //有错的话,就返回登录页面。
return "user/login";
}
return "user/list";
}
}
四.五 前端显示代码
<body>
<h2>两个蝴蝶飞,数据验证使用</h2>
<form:form commandName="user" action="login.action" method="post">
<form:label path="name">姓名:</form:label>
<form:input path="name"/>
<form:errors path="name"></form:errors> <br/>
<form:label path="password">密码:</form:label>
<form:input path="password"/> <!--为了能看见值,这里用 form:input,而不是form:password -->
<form:errors path="password"></form:errors> <br/>
<form:label path="age">年龄:</form:label>
<form:input path="age"/>
<form:errors path="age"></form:errors> <br/>
<form:label path="description">描述:</form:label>
<form:input path="description"/>
<form:errors path="description"></form:errors> <br/>
<form:label path="score">成绩:</form:label>
<form:input path="score"/>
<form:errors path="score"></form:errors> <br/>
<form:label path="birthday">出生日期:</form:label>
<form:input path="birthday"/>
<form:errors path="birthday"></form:errors> <br/>
<form:label path="phone">联系方式:</form:label>
<form:input path="phone"/>
<form:errors path="phone"></form:errors> <br/>
<form:label path="email">邮箱:</form:label>
<form:input path="email"/>
<form:errors path="email"></form:errors> <br/>
<form:button>提交</form:button>
</form:form>
</body>
四.六 重启服务器,进行验证
情况组合太多,老蝴蝶在这里只简单列举几个。
JSR 303验证是正确的,可以进行验证。
五. JSR 303 验证分组
把 User 中的属性上面都放置了注解,这个User 被很多 Action 调用,在校验的时候,有的是不需要校验的,有的是必须要校验的, 如password 属性,只在登录的Action 校验, 在修改资料的Action 中是不校验的,还有id属性, 在新增的方法中是不校验的, 在修改的方法中是需要校验的。 需要让User 中的校验器可以根据不同的情况,进行不同的划分。 需要添加不同的校验组。 每个Action的每个方法根据自己的不同验证来调用不同的校验组。
五.一 根据验证情况,创建不同的分组接口
老蝴蝶在这儿只简单创建两个
UserGroup1.class
package com.yjl.validator;
public interface UserGroup1 {
//没有任何方法,也不实现任何接口,只是做个标记而已。
}
UserGroup2.class
package com.yjl.validator;
public interface UserGroup2 {
//没有任何方法,也不实现任何接口,只是做个标记而已。
}
五.二 在User.class 中为每一个属性的每一个注解分配组
老蝴蝶在这儿,只简单用 name和password 来举例子,其他的属性不配置组,不进行验证。
User.class
@NotBlank(message="用户名不能为空",groups={UserGroup1.class,UserGroup2.class})
@Length(max=10,message="用户名不能超过10个字",groups={UserGroup1.class})
private String name;
@NotBlank(message="密码不能为空",groups={UserGroup2.class})
@Length(min=6,max=15,message="密码长度应在6~15之间",groups={UserGroup2.class})
private String password;
可知, 在usergroup1 中只验证 用户名和用户名长度, 在usergroup2 中验证用户名不为空和密码不为空,密码的长度。
五.三 Action 方法中添加不同的分组
UserAction.class 中,只在 @Validated 中添加value的属性即可。
@RequestMapping(value="login")
public String login(@Validated(value={UserGroup1.class}) User user,
BindingResult bindingResult,Model model){
model.addAttribute("user",user);
if(bindingResult.hasErrors()){ //有错的话,就返回登录页面。
return "user/login";
}
return "user/list";
}
五.四 重启服务器,进行校验
密码可以不输入便可通过。
五.五 将分组变成 UserGroup2.class 进行验证
@RequestMapping(value="login")
public String login(@Validated(value={UserGroup2.class}) User user,
BindingResult bindingResult,Model model){
model.addAttribute("user",user);
if(bindingResult.hasErrors()){ //有错的话,就返回登录页面。
return "user/login";
}
return "user/list";
}
重启服务器,进行验证。
六. 验证信息国际化
在提示的错误信息,在国际化的系统中,也需要进行国际化。
可结合上一章的国际化来进行学习。
六.一 创建国际化验证提示信息文件
i18n 为源文件夹。
默认的 UserMessage.properties
user.name.null=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
user.name.length=\u7528\u6237\u540D\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC710
user.password.null=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
user.password.length=\u5BC6\u7801\u957F\u5EA6\u5E94\u57286~15\u4E4B\u95F4
UserMessage_zh_CN.properties
user.name.null=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
user.name.length=\u7528\u6237\u540D\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC710
user.password.null=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
user.password.length=\u5BC6\u7801\u957F\u5EA6\u5E94\u57286~15\u4E4B\u95F4
UserMessage_en_US.properites
user.name.null=name is not null
user.name.length=The user name cannot be longer than 10
user.password.null=password is not null
user.password.length=Password length should be between 6 and 15
六.二 User.java 中 message内容改成国际化
老蝴蝶这里也是针对 name 和password 进行验证
@NotBlank(message="{user.name.null}",groups={UserGroup1.class,UserGroup2.class})
@Length(max=10,message="{user.name.length}",groups={UserGroup1.class})
private String name;
@NotBlank(message="{user.password.null}",groups={UserGroup2.class})
@Length(min=6,max=15,message="{user.password.length}",groups={UserGroup2.class})
private String password;
UserAction 中的内容不变,与五.五 部分的内容一致。
六.三 springmvc.xml 文件中配置资源文件路径
<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"></property>
<!-- 国际化文件 -->
<property name="validationMessageSource" ref="messageSource">
</property>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:UserMessage</value>
<!-- 可继续添加多个 -->
</list>
</property>
<!-- 资源文件编码格式 -->
<property name="fileEncodings" value="utf-8"/>
<!-- 资源文件的缓存时间,单位是s 秒 -->
<property name="cacheSeconds" value="120"/>
</bean>
六.四 重启服务器,进行验证
刚开始浏览器环境是中文的
提交后:
更改成英文的环境,(更改环境,上一章已经讲过了, 选项,常规,语言,改成英文,应用并重启) 提交后:
发现,仍然是中文的,并没有改成英文的。
这种方式的国际化发现失败了。 找了很长时间的错误,不知道,是为什么。
后来,用 Session 的国际化实现了这一操作。
(六.三 和六.四 这两个步骤是错误的。 暂时不删除,老蝴蝶为了警示自己)
六.三(正确的) 写前端代码,添加 中文和英文的链接
<body>
<h2>两个蝴蝶飞,数据验证使用</h2>
<a href="https://www.ctyun.cn/portal/link.html?target=toLogin.action%3Frequest_locale%3Dzh_CN">中文</a>
<a href="https://www.ctyun.cn/portal/link.html?target=toLogin.action%3Frequest_locale%3Den_US">美国</a>
<form:form commandName="user" action="login.action" method="post">
<form:label path="name">姓名:</form:label>
<form:input path="name"/>
<form:errors path="name"></form:errors> <br/>
<form:label path="password">密码:</form:label>
<form:input path="password"/> <!--为了能看见值,这里用 form:input,而不是form:password -->
<form:errors path="password"></form:errors> <br/>
<form:label path="age">年龄:</form:label>
<form:input path="age"/>
<form:errors path="age"></form:errors> <br/>
<form:label path="description">描述:</form:label>
<form:input path="description"/>
<form:errors path="description"></form:errors> <br/>
<form:label path="score">成绩:</form:label>
<form:input path="score"/>
<form:errors path="score"></form:errors> <br/>
<form:label path="birthday">出生日期:</form:label>
<form:input path="birthday"/>
<form:errors path="birthday"></form:errors> <br/>
<form:label path="phone">联系方式:</form:label>
<form:input path="phone"/>
<form:errors path="phone"></form:errors> <br/>
<form:label path="email">邮箱:</form:label>
<form:input path="email"/>
<form:errors path="email"></form:errors> <br/>
<form:button>提交</form:button>
</form:form>
</body>
六.四 (正确) springmvc.xml 配置文件中写 Hibernate验证和国际化
<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"></property>
<!-- 国际化文件 -->
<property name="validationMessageSource" ref="messageSource">
</property>
</bean>
<!-- 配置国际化的拦截器 -->
<mvc:interceptors>
<!-- 必须配置 -->
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
</mvc:interceptors>
<!-- 配置 SessionLocaleResolver 国际化bean,必须写 -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>UserMessage</value>
</list>
</property>
</bean>
六.五 后端UserAction 代码
//转到登录的页面
@RequestMapping(value="toLogin")
public String toLogin(String request_locale,Model model,HttpServletRequest request){
if(request_locale!=null&&!"".equals(request_locale)){
Locale locale=null;
if("zh_CN".equals(request_locale)){
locale=new Locale("zh","CN");
}else if("en_US".equals(request_locale)){
locale=new Locale("en","US");
}else{
locale=LocaleContextHolder.getLocale();
}
request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,
locale);
}else{
request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,
LocaleContextHolder.getLocale());
}
model.addAttribute("user",new User());
return "user/login";
}
//绑定到user对象。
@RequestMapping(value="login")
public String login(@Validated(value={UserGroup2.class}) User user,
BindingResult bindingResult,Model model){
model.addAttribute("user",user);
if(bindingResult.hasErrors()){ //有错的话,就返回登录页面。
return "user/login";
}
return "user/list";
}
六.六 重启服务器,进行验证
刚开始是中文的,图片显示
点击链接,换成英文的,图片显示
国际化显示提示信息正确。
谢谢!!!