b/s系统中对http请求数据的校验多数在客户端进行,这也是出于简单及用户体验性上考虑,但是在一些安全性要求高的系统中服务端校验是不可缺少的,本节主要学习springmvc实现控制层添加校验。
Spring3支持JSR-303验证框架,JSR-303 是JAVA EE 6 中的一项子规范,叫做BeanValidation,官方参考实现是Hibernate Validator(与Hibernate ORM 没有关系),JSR 303 用于对Java Bean 中的字段的值进行验证。
项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。
服务端校验:(面试)
控制层conroller:校验页面请求的参数的合法性。在服务端控制层conroller校验,不区分客户端类型(浏览器、手机客户端、远程调用)
业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。
持久层dao:一般是不校验的。
springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。
校验思路:
页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。
具体需求:
商品修改,添加校验(校验商品名称长度,生产日期的非空校验),如果校验出错,在商品修改页面显示错误信息。
1. 配置校验信息倒入Jar配置springmvc和注入容器
2. 配置文件proterties
3. 在pojo类中写校验
4. 在controller中接收
5. 传递给页面
hibernate的校验框架validation所需要jar包:
适配器执行handel 所以将校验器注入到处理器适配器中
在sprigmvc.Xml中配置
<--校验器 -->
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" >
<--校验器-->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<--指定校验使用的资源文件,如果不指定则默认使用classpath下的ValidationMessages.properties -->
<property name="validationMessageSource" ref="messageSource" />
</bean>
<--校验错误信息配置文 -->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<--资源文件名-->
<property name="basenames">
<list>
<value>classpath:CustomValidationMessages <value>
</list>
</property>
<--资源文件编码格式 -->
<property name="fileEncodings"value="utf-8" />
<!-- 对资源文件内容缓存时间,单位秒 -->
<property name="cacheSeconds"value="120" />
</bean>
<mvc:annotation-driven conversion-service="conversionService"
validator="validator"></mvc:annotation-driven>
<!-- 自定义webBinder -->
<bean id="customBinder"
class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="validator"ref="validator" />
</bean>
<!-- 注解适配器 -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer"ref="customBinder"></property>
</bean>
在ItemsCustom.java中添加校验规则:
public classItems {
privateInteger id;
//校验名称在1-30字符中间
//message是错误提示验证出错的信息
@Size(min=1,max=30,message="{items.name.length.error}")
private String name;
private Float price;
private String pic;
//非空校验
@NotNull(message="{items.createtime.isNull}")
private Date createtime;
private String detail;
在CustomValidationMessages.properties配置校验错误信息:
items.name.length.error=请输入1到30个字符的商品名称
items.createtime.isNull=请输入商品的生产日期
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit(HttpServletRequest request,Integer id,@Validated ItemsCustom itemsCustom,BindingResult bindingResult)throws Exception{
//在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult bindingResult接收校验出错信息
//注意:@Validated和BindingResultbindingResult是配对出现,并且形参顺序是固定的(一前一后)。
在controller中将错误信息传到页面即可。
@RequestMapping("/editItemsSubmit")
//如果需要同时穿商品的name和用户的name 则需要用包装的pojo
//在需要校验的pojo前边添加@Validated在需要校验的pojo后边添加BindingResult来接收校验出错的信息
//注意:@Validated和@Validated是配对出现,并且形参是顺序是固定的(一前一后)
public String editItemsSubmit(Model model
,HttpServletRequest request,Integer id,@Validated ItemsCustomitemsCustom,BindingResult bindingResult)throws Exception{
//获取验证错误信息
if(bindingResult.hasErrors())
{
//输出错误信息
List<ObjectError> allerrors=bindingResult.getAllErrors();
for(ObjectError error: allerrors)
{
System.out.println(error.getDefaultMessage());
}
//错误信息传递到页面
model.addAttribute("allErrors", allerrors);
return "items/editItems";
}
//调用service更新商品信息,页面需要将商品信息传到此方法
itemsService.updateItems(id, itemsCustom);
//重定向 不用加跟路径
//return"redirect:queryItems.action";
//页面转发
return "forward:queryItems.action";
}
页面显示错误信息:
<!-- 显示错误信息 -->
<div style="color:red">
<c:if test="${allErrors!=null}">
<c:forEach items="${allErrors}"var="error">
${error.defaultMessage}</br>
</c:forEach>
</c:if>
</div>
在pojo中定义校验规则,而pojo是被多个 controller所共用,当不同的controller方法对同一个pojo进行校验,但是每个controller方法需要不同的校验。
解决方法:
定义多个校验分组(其实是一个java接口),分组中定义有哪些规则
每个controller方法使用不同的校验分组
1. 定义校验分组接口
2. 在校验规则中添加分组
3. 在controller 中使用分组校验
/**
* 校验分组
* @author Administrator
*
*/
public interface ValidGrouop1 {
//接口中需要定义任何方法,仅是对不同的校验规则进行分组
//此分组只校验上商品名称长度
}
//校验名称在1-30字符中间
//message是错误提示验证出错的信息
//groups校验是属于哪个分组,group可以定义多个分组
@Size(min=1,max=30,message="{items.name.length.error}",groups={ValidGrouop1.class})
private String name;
@RequestMapping("/editItemsSubmit")
//如果需要同时穿商品的name和用户的name 则需要用包装的pojo
//在需要校验的pojo前边添加@Validated在需要校验的pojo后边添加BindingResult来接收校验出错的信息
//注意:@Validated和@Validated是配对出现,并且形参是顺序是固定的(一前一后)
//ValidGrouop1.class指定使用分组的校验
public String editItemsSubmit(Model model
,HttpServletRequest request,Integer id,
@Validated(value={ValidGrouop1.class}) ItemsCustomitemsCustom,BindingResult bindingResult)throws Exception{
摘自传智博客燕青老师的视频