数据校验
校验的理解:
项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。
服务端校验:
- 控制层conroller:校验页面请求的参数的合法性。在服务端控制层conroller校验,不区分客户端类型(浏览器、手机客户端、远程调用)
- 业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。
- 持久层dao:一般是不校验的。
springmvc校验需求:springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。
校验思路: 页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。
此处做了两种情况
- 检验单一controller:pojo封装对象校验
- 分组校验多个controller:pojo对象属性校验
单一校验:pojo对象属性校验
需求
本项目介绍的具体需求: 商品修改,添加校验(校验商品名称长度,生产日期的非空校验),如果校验出错,在商品修改页面显示错误信息。
环境准备
校验的三个核心jar包
配置校验器
<!-- 校验器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- hibernate校验器 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
<!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如不指定,则默认classpath下的validationMessage.properties文件 -->
<property name="validationMessageSource" ref="messageSource"></property>
</bean>
<!-- 校验错误信息配置文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 资源文件名 -->
<property name="basenames">
<list>
<!-- 配置错误信息文件 CustomValidationMessages.properties-->
<value>classpath:CustomValidationMessages</value>
</list>
</property>
<!-- 解决中文乱码 -->
<property name="defaultEncoding" value="utf-8"></property>
<!-- 资源文件编码格式 -->
<property name="fileEncodings" value="utf-8"></property>
<!-- 资源文件缓存时间 -->
<property name="cacheSeconds" value="120"></property>
</bean>
校验器注入处理器适配器中
<!-- 自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapte两个bean,
是spring MVC为@Controllers分发请求所必须的 -->
<mvc:annotation-driven conversion-service="conversionService" validator="validator" />
在pojo中编写错误规则
// 商品名称在1-10个字符,message校验信息出错提示
@Size(min=1,max=10,message="{items.items_name.length.error}")
private String items_name; // 商品名称
// 非空校验
@NotNull(message="{items.items_price.isNull}")
private Double items_price; // 商品价格
编写错误信息
这里注意:很多情况下java properties file中默认编码格式是:iso-8859-1,校验信息会出现乱码情况,需要改为UTF-8才能识别中文注释
解决:Eclipse-Window-Preferences-General-Content Types-Text-Java Properties File-UTF-8
#添加校验错误提示信息
items.items_name.length.error=请输入1-10个字符的商品
items.items_price.isNull=请输入商品价格!
捕获校验错误信息
在需要校验的pojo前边添加@Validated,后边添加BindingResult bindingResult接收校验出错信息
注意:
- @Validated和BindingResult bindingResult是配对出现。
- 这里采用的是pojo的封装方式,所以需要@Valid来标识pojo的校验
/**
*
* @Description: 提交批量修改商品信息
* 绑定原理:通过ItemsQueryVo批量提交商品信息,将商品信息存储在ItemsQueryVo的itemsList属性中
* @param @param request 通过request对象获取请求信息
* @param @param itemsQueryVo pojo绑定
* @param @return 返回商品信息页面
* @param @throws Exception
* @return ModelAndView
* @throws
* @author XHChen
* @date 2018年10月26日 下午9:30:23
*/
@RequestMapping("/editItemsAllSubmit.action")
public ModelAndView editItemsAllSubmit(HttpServletRequest request,
@Validated ItemsQueryVo itemsQueryVo, BindingResult bindingResult)
throws Exception {
// 返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
// 有校验错误
if (bindingResult.hasErrors()) {
// 获得脚亚目错误信息
List<ObjectError> objectErrors = bindingResult.getAllErrors();
// 页面提示错误信息
// 添加错误信息
modelAndView.addObject("objectErrors", objectErrors);
// 重新查询所有商品信息
List<ItemsCustomer> itemsList = itemsService.findItemsList(itemsQueryVo);
// 返回数据
modelAndView.addObject("itemsList", itemsList);
// 指定视图
modelAndView.setViewName("items/editItemsQuery");
} else {
// 没有校验错误
// 调用itemsService接口方法
// 逐条提交批量修改商品信息
itemsService.updateAllItems(itemsQueryVo.getItemsList());
// 一次性提交批量修改商品信息,需要sql拼接
// itemsService.updateAllItemsSubmit(itemsQueryVo.getItemsList());
// 重新查询所有商品信息
List<ItemsCustomer> itemsList = itemsService.findItemsList(itemsQueryVo);
// 向页面传递数据
modelAndView.addObject("itemsList", itemsList);
// 指定视图
modelAndView.setViewName("items/itemsList");
}
// 返回视图
return modelAndView;
}
在ItemsQueryVo.java商品信息包装类中标识校验
// 接受list集合,批量商品信息
@Valid
private List<ItemsCustomer> itemsList;
在页面显示校验错误信息
<c:if test="${objectErrors!=null }">
<c:forEach items="${objectErrors}" var="error">
${error.defaultMessage }
</c:forEach>
</c:if>
分组校验:pojo封装对象校验
需求
在pojo中定义校验规则,而pojo是被多个 controller所共用,当不同的controller方法对同一个pojo进行校验,但是每个controller方法需要不同的校验。
解决方法:
定义多个校验分组(其实是一个java接口),分组中定义有哪些规则
每个controller方法使用不同的校验分组
校验分组
在cn.ssm.xhchen.validaGroups包下创建检验分组接口,接口不做任何方法,仅对校验进行分组
商品名称校验接口
package cn.ssm.xhchen.validaGroups;
/**
*
* ClassName: ValidaGroup1
*
* @Description: 商品名称校验
* @author XHChen
* @date 2018年10月31日 下午9:16:56
*/
public interface ValidaGroupItemsName {}
商品价格校验校验接口
package cn.ssm.xhchen.validaGroups;
/**
*
* ClassName: ValidaGroupItems_Price
*
* @Description: 价格校验
* @author XHChen
* @date 2018年10月31日 下午9:20:50
*/
public interface ValidaGroupItems_Price {}
添加校验分组
// 商品名称在1-10个字符,message校验信息出错提示
@Size(min=1,max=10,message="{items.items_name.length.error}",groups=(ValidaGroupItemsName.class))
private String items_name; // 商品名称
// 非空校验
@NotNull(message="{items.items_price.isNull}",groups=(ValidaGroupItems_Price.class))
private Double items_price; // 商品价格
Controller指定使用分组
在需要校验的pojo前边添加@Validated,后边添加BindingResult bindingResult接收校验出错信息
注意:
- @Validated和BindingResult bindingResult是配对出现。
- @Validated(value=ValidaGroupItemsName.class)指定校验分组
- 单一controller不需要@Valid来标识pojo的校验
/**
*
* @Description: 修改商品信息
* @param @param request 通过request对象获取请求信息
* @param @param id 商品信息id
* @param @param itemsCustomer
* @param @return 返回商品列表
* @param @throws Exception
* @return ModelAndView
* @throws
* @author XHChen
* @date 2018年10月26日 上午10:18:14
*/
@RequestMapping("/editItemsSubmit.action")
public ModelAndView editItemsSubmit(HttpServletRequest request, Integer id,
@Validated(value=ValidaGroupItems_Price.class) ItemsCustomer itemsCustomer,BindingResult bindingResult,
ItemsQueryVo itemsQueryVo) throws Exception {
// 返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
// 获得校验错误信息
if (bindingResult.hasErrors()) {
List<ObjectError> objectErrors = bindingResult.getAllErrors();
// 显示错误信息但页面
modelAndView.addObject("objectErrors", objectErrors);
// 指定视图
modelAndView.setViewName("items/editItems");
} else {
// 调用itemsService方法修改商品信息,需要将页面数据提交到此方法
itemsService.updateItems(id, itemsCustomer);
// 重新查询商品信息
List<ItemsCustomer> itemsList = itemsService.findItemsList(itemsQueryVo);
// 返回参数到页面
modelAndView.addObject("itemsList", itemsList);
// 指定视图
modelAndView.setViewName("items/itemsList");
}
// 返回指定视图
return modelAndView;
}
数据回显
什么数据回显: 提交后,如果出现错误,将刚才提交的数据回显到刚才的提交页面。
pojo数据回显方法
- springmvc默认对pojo数据进行回显。 pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写) 使用@ModelAttribute指定pojo回显到页面在request中的key。
- @ModelAttribute还可以将方法的返回值传到页面 在商品查询列表页面,通过商品类型查询商品信息。 在controller中定义商品类型查询方法,最终将商品类型传到页面。
// 商品分类
// @ModelAttribute最终讲方法返回值放在value的key中
@ModelAttribute("itemTypes")
public Map<String, String> getItemTypes() {
Map<String, String> getItemTypes = new HashMap<String, String>();
getItemTypes.put("001", "电脑");
getItemTypes.put("002", "手机");
return getItemTypes;
}
页面显示itemtypes数据
<h3>商品类型</h3>
<select name="itemTypes">
<c:forEach items="${itemTypes }" var="itemTypes">
<option value="${itemTypes.key }">${itemTypes.value }</option>
</c:forEach>
</select>
- 使用最简单方法使用model,可以不用@ModelAttribute。
// 获得校验错误信息
if (bindingResult.hasErrors()) {
List<ObjectError> objectErrors = bindingResult.getAllErrors();
// 显示错误信息但页面
model.addAttribute("objectErrors", objectErrors);
// 显示错误信息但页面
model.addAttribute("items", itemsCustomer);
// 返回指定视图
return "items/editItems";
}