所有的验证都是要先注册验证器,不过验证器也是SpringMVC自动加载的。这里笔者下载了关于验证器所需的jar包,包括classmate-1.3.3.jar、jboss-logging-3.3.1.Final.jar、hibernate-validator-5.4.1.Final.jar和validation-api-1.1.0.Final.jar。其中,validation-api-1.1.0. Final.jar提供关于验证注解的,它只有一些定义,而没有实现;hibernate-validator-5.4.1. Final.jar是通过Hibernate检验规则的包,它的运行还依赖于classmate-1.3.3.jar和jboss-logging-3.3.1.Final.jar这两个包。我们使用Hibernate检验规则把这些包加载进来。
<!-- 验证--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.1.Final</version> </dependency>
使用JSR 303注解验证输入内容
Spring提供了对Bean的功能校验,通过注解@Valid标明哪个Bean需要启用注解式的验证。在javax.validation.con-straints.*中定义了一系列的JSR 303规范给出的注解,在使用它们之前需要对这些注解有一定的了解,如表所示。
代码清单15-35:交易表单
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>validate</title> </head> <body> <%--<form action="../validate/annotation.do">--%> <form action="../validate/validator.do"> <table> <tr> <td>产品编号:</td> <td><input name="productId" id="productId" value="12"/></td> </tr> <tr> <td>用户编号:</td> <td><input name="userId" id="userId" value="1231"/></td> </tr> <tr> <td>交易日期:</td> <td><input name="date" id="date" value="2019-06-30"/></td> </tr> <tr> <td>价格:</td> <td><input name="price" id="price" value="12323"/></td> </tr> <tr> <td>数量:</td> <td><input name="quantity" id="quantity" value="500"/></td> </tr> <tr> <td>交易金额:</td> <td><input name="amount" id="amount" value="3523"/></td> </tr> <tr> <td>用户邮件:</td> <td><input name="email" id="email" value="email"/></td> </tr> <tr> <td>备注:</td> <td><textarea id="note" name="note" cols="20" rows="5">我的世界</textarea></td> </tr> <tr> <td colspan="2" align="right"><input type="submit" value="提交"/> </tr> </table> </form> </body> </html>
代码清单15-36:表单POJO
package com.ssm.chapter15.pojo; import org.springframework.format.annotation.DateTimeFormat; import javax.validation.constraints.*; import java.util.Date; public class Transaction { // 产品编号 @NotNull // 不能为空 private Long productId; // 用户编号 @NotNull // 不能为空 private Long userId; // 交易日期 @Future // 只能是将来的日期 @DateTimeFormat(pattern = "yyyy-MM-dd") // 日期格式化转 @NotNull // 不能为空 private Date date; // 价格 @NotNull // 不能为空 @DecimalMin(value = "0.1") // 最小值0.1 private Double price; // 数量 @Min(1) // 最小值为1 @Max(100) // 最大值 @NotNull // 不能为空 private Integer quantity; // 交易金额 @NotNull // 不能为空 @DecimalMax("500000.00") // 最大金额为5万元 @DecimalMin("1.00") // 最小交易金额1元 private Double amount; // 邮件 @Pattern( // 正则式 regexp = "^([a-zA-Z0-9]*[-_]?[a-zA-Z0-9]+)*@" + "([a-zA-Z0-9]*[-_]?[a-zA-Z0-9]+)+[\\.][A-Za-z]{2,3}([\\.] [A-Za-z]{2})?$", // 自定义消息提示 message = "不符合邮件格式") private String email; // 备注 @Size(min = 0, max = 256)// 0到255个字 private String note; @NotNull public Long getProductId() { return productId; } public void setProductId(@NotNull Long productId) { this.productId = productId; } @NotNull public Long getUserId() { return userId; } public void setUserId(@NotNull Long userId) { this.userId = userId; } @NotNull public Date getDate() { return date; } public void setDate(@NotNull Date date) { this.date = date; } @NotNull public Double getPrice() { return price; } public void setPrice(@NotNull Double price) { this.price = price; } @NotNull public Integer getQuantity() { return quantity; } public void setQuantity(@NotNull Integer quantity) { this.quantity = quantity; } @NotNull public Double getAmount() { return amount; } public void setAmount(@NotNull Double amount) { this.amount = amount; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } @Override public String toString() { return "Transaction{" + "productId=" + productId + ", userId=" + userId + ", date=" + date + ", price=" + price + ", quantity=" + quantity + ", amount=" + amount + ", email='" + email + '\'' + ", note='" + note + '\'' + '}'; } }
代码清单15-37:用控制器验证表单
package com.ssm.chapter15.controller; import com.ssm.chapter15.pojo.Transaction; import org.springframework.stereotype.Controller; import org.springframework.validation.Errors; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import javax.validation.Valid; import java.util.List; @Controller @RequestMapping("/validate") public class ValidateController { @RequestMapping("/annotation") public ModelAndView annotationValidate(@Valid Transaction trans, Errors errors) { //是否存在错误 if (errors.hasErrors()) { // 获取错误信息 List<FieldError> errorList = errors.getFieldErrors(); for (FieldError error : errorList) { //打印字段错误信息 System.err.println("fied :" + error.getField() + "\t" + "msg:" + error.getDefaultMessage()); } } ModelAndView mv = new ModelAndView(); mv.setViewName("index"); return mv; } }
使用验证器
有时候除了简单的输入格式、非空性等校验,也需要一定的业务校验,Spring提供了Validator接口来实现检验,它将在进入控制器逻辑之前对参数的合法性进行检验。代码清单15-39:交易验证器
package com.ssm.chapter15.validator; import com.ssm.chapter15.pojo.Transaction; import org.springframework.validation.Errors; import org.springframework.validation.Validator; public class TransactionValidator implements Validator { @Override public boolean supports(Class<?> clazz) { //判断验证是否为Transaction,如果是则进行验证 return Transaction.class.equals(clazz); } @Override public void validate(Object target, Errors errors) { Transaction trans = (Transaction) target; // 求交易金额和价格×数量的差额 double dis = trans.getAmount() - (trans.getPrice() * trans.getQuantity()); //如果差额大于0.01,则认为业务错误 if (Math.abs(dis) > 0.01) { // 加入错误信息 errors.rejectValue("amount", null, "交易金额和购买数量与价格不匹配"); } } }
这样这个验证器就判断了是否Transaction对象,如果是才去验证后面的逻辑,那么要将它捆绑到对应的控制器中,这个时候Spring MVC提供了注解@InitBinder,后文我们会再次详细讲解@InitBinder。通过它就可以将验证器和控制器捆绑到一起,这样就能够对请求表单进行验证了。对于@InitBinder的使用还有其他的内容,这里只展示其捆绑验证器的方法。
代码清单15-40:使用验证器验证
@InitBinder public void initBinder(DataBinder binder) { //数据绑定器加入验证器 binder.setValidator(new TransactionValidator()); } @RequestMapping("/validator") public ModelAndView validator(@Valid Transaction trans, Errors errors) { //是否存在错误 if (errors.hasErrors()) { // 获取错误信息 List<FieldError> errorList = errors.getFieldErrors(); for (FieldError error : errorList) { // 打印字段错误信息 System.err.println("fied :" + error.getField() + "\t" + "msg:" + error.getDefaultMessage()); } } ModelAndView mv = new ModelAndView(); mv.setViewName("index"); return mv; }
比较遗憾的是,JSR 303注解方式和验证器方式不能同时使用,不过可以在使用JSR 303注解方式得到基本的检验信息后,再使用自己的方法进行验证。