表单标签库
<%@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 该属性值必须在指定范围内