表单标签库
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<form:form commandName="book" action="saveBook" method="post"> </form:form> <form:input id="city" path="city" /> <form:password id="pwd" path="password" cssClass="normal" /> <form:hidden path="productId" /> <form:textarea path="note" tabindex="4" rows="5" cols="80" /> <form:checkbox path="outOfStock" value="out of stock" /> <form:radiobutton path="newsletter" value="computing now" /> <form:radiobutton path="newsletter" value="modern health" /> <form:checkboxes path="category" items="${categoryList}" /> <form:radiobuttons path="category" items="${categoryList}" /> <form:select id="category" path="category.id" items="${categories}" itemLabel="name" itemValue="id" > <option value="0">-- please select --</option> </form:select> <form:errors path="*" /><!--显示了所有字段的错误信息--> <form:errors path="author" /><!--显示了author属性相关的错误信息-->
jstl
<%@taglib prefix="c" uri="http://java.sun.com/jsp/core" %>
转化器和格式化
转换器converter,需要实现org.springframework.core.convert.converter.Converter接口
格式化formatter,需要实现org.springframework.format.Formatter接口
可以利用转化器和格式化来引导spring mvc应用程序中的数据绑定。Converter是一般工具,可以将任意类型转换成另一种类型,而Formatter只能将String类型转换成另一种Java类型。Formatter更适合于web层。
验证器
在spring mvc中,有两种方式可以验证输入,一是利用spring自带的验证框架,二是利用JSR 303实现。
Converter和Formatter作用于field级,在mvc应用程序中,将String转换或格式化成另一种Java类型;验证器作用于object级,决定某一个对象中的所有field是否均是有效的以及是否遵循某些规则。
如果一个应用程序中既使用了Formatter又使用了Validator,那么在调用Controller期间,先格式化,格式化完成之后验证器介入。
验证器要实现org.springframework.validation.Validator接口。
指定的验证器处理指定的Class文件,supports方法将返回true,validate方法会验证目标对象,并将验证错误填入Errors对象中。
工具类org.springframework.validation.ValidationUtils有助于编写spring验证器。
if(firstName==null || firstName.isEmpty()){ errors.rejectValue("price"); } // 相当于 ValidationUtils.rejectIfEmpty("price"); if(firstName==null || firstName.trim().isEmpty()){ errors.rejectValue("price"); } // 相当于 ValidationUtils.rejectIfEmptyOrWhitespace("price");
package com.xsl.springmvc; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; public class ProductValidator implements Validator { @Override public boolean supports(Class<?> arg0) { return Product.class.isAssignableFrom(arg0); } @Override public void validate(Object arg0, Errors arg1) { Product product = (Product) arg0; ValidationUtils.rejectIfEmpty(arg1, "name", "productname.required"); ValidationUtils.rejectIfEmpty(arg1, "price", "price.required"); Float price = product.getPrice(); if(price != null && price < 0){ arg1.rejectValue("price", "price.negative"); } } }
写好验证器类之后,如果想要从某个属性文件中获取错误信息,则需要通过声明messageSource bean,告诉spring要去哪里找这个文件,例如:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basename" value="/WEB-INF/resource/messages" /> </bean>
上面代码意思是,错误码和错误信息可以在/WEB-INF/resource目录下的messages.properties文件中找到,例如:
productname.required=please enter a product name
price.required=please enter a price
date.invalid=invalid date
验证的三种方法:
方法一:new出验证器类对象,调用validate方法,并在BindingResult中调用hasErrors方法看是否生成错误信息。
package com.xsl.springmvc; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class ProductController { private static final Log logger = LogFactory.getLog(ProductController.class); @RequestMapping(value="/saveProduct") public String saveProduct(@ModelAttribute Product product,BindingResult bindingResult,Model model){ ProductValidator validator = new ProductValidator(); validator.validate(product, bindingResult); if(bindingResult.hasErrors()){ FieldError fieldError = bindingResult.getFieldError(); logger.info("code:"+fieldError.getCode()+",field:"+fieldError.getField()); return "productForm"; } model.addAttribute("product", product); return "productDetails"; } }
方法二:在controller中编写initBinder方法,并将验证器传到WebDataBinder,并调用其validate方法,会使验证器应用于Controller类中所有处理请求的方法。
@InitBinder public void initBinder(WebDataBinder binder){ // this will apply the validation to all request-handing methods binder.setValidator(new ProductValidator()); binder.validate(); }
方法三:利用@javax.validation.Valid对要验证的对象参数进行标注(Valid标注类型是在JSR 303中定义的)。
实体类上面嵌入约束
package com.xsl.springmvc; import java.io.Serializable; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public class Product implements Serializable { private static final long serialVersionUID = 748392348L; @Size(min=2,max=10) private String name; @NotNull private String description; @Min(0) private float price; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } }
为了覆盖JSR303的错误信息,可以在属性文件中使用键,例如:
Size.product.name、NotNull.product.description、Min.product.price
在controller中,必须要对调用的方法的参数进行标注
@RequestMapping(value="/saveProduct") public String saveProduct(@Valid @ModelAttribute Product product,BindingResult bindingResult,Model model){ if(bindingResult.hasErrors()){ FieldError fieldError = bindingResult.getFieldError(); logger.info("code:"+fieldError.getCode()+",field:"+fieldError.getField()); return "productForm"; } model.addAttribute("product", product); return "productDetails"; }
为了定制来自验证器的错误信息,要在messages.properties文件中使用两个键。
Size.product.name = name must be 2 to 10 characters long
NotNull.product.description = description must be not null
Min.product.price = price must be greater than 0
JSR303约束
@AssertFalse 应用于boolean属性,该属性值必须为false
@AssertTrue 应用于boolean属性,该属性值必须为true
@DecimalMax 该属性值必须为小于或等于指定值的小数
@DecimalMin 该属性值必须为大于或等于指定值的小数
@Digits 该属性值必须在指定范围内,integer属性定义该数值的最大整数部分,fraction属性定义该数值的最大小数部分,例如@Digits(integer=5,fraction=2)
@Future 该属性值必须是未来的一个日期
@Max 该属性值必须是一个小于或等于指定值的整数
@Min 该属性值必须是一个大于或等于指定值的整数
@NotNull 该属性值不能为null
@Null 该属性值必须为null
@Past 该属性值必须是过去的一个日期
@Pattern 该属性值必须与指定的常规表达式相匹配
@Size 该属性值必须在指定范围内