springMVC学习笔记四(数据类型转换和数据验证)

=============================数据类型转换和数据验证=======================
数据类型转换


Spring 内建的 PropertyEditor 如下所示:
类名                         说明                                默认是否注册
ByteArrayPropertyEditor  String<——>byte[]                            √ 
ClassEditor              String<——>Class 
                       当类没有发现抛出 IllegalArgumentException       √ 
CustomBooleanEditor      String<——>Boolean 
                true/yes/on/1 转换为 true,false/no/off/0 转换为 false √ 
CustomCollectionEditor  数组/Collection——>Collection 
普通值——>Collection(只包含一个对象) 
如 String——>Collection 
不允许 Collection——>String(单方向转换)      √ 
CustomNumberEditor      String<——>Number(Integer、Long、Double)       √ 
FileEditor              String<——>File                                √ 
InputStreamEditor       String——>InputStream 
                        单向的,不能 InputStream——>String             √ 
LocaleEditor            String<——>Locale, 
                (String 的形式为[语言]_[国家]_[变量],这与 Local 对象的
                        toString()方法得到的结果相同)                  √ 
PatternEditor            String<——>Pattern                            √ 
PropertiesEditor         String<——>java.lang.Properties               √ 
URLEditor                String<——>URL                                √ 
StringTrimmerEditor      一个用于 trim 的 String 类型的属性编辑器 
                        如默认删除两边的空格,charsToDelete 属性:
                        可以设置为其他字符 emptyAsNull 属性:
                         将一个空字符串转化为 null 值的选项。            × 
CustomDateEditor         String<——>java.util.Date                      × 






Spring 内建的 PropertyEditor 支持的属性(符合 JavaBean 规范)操作: 
表达式                          设值/取值说明 
username                        属性 username 
                               设值方法 setUsername()/取值方法       getUsername() 或 isUsername() 
schooInfo.schoolType           属性 schooInfo 的嵌套属性 schoolType 
设值方法 getSchooInfo().setSchoolType()
取值方法 getSchooInfo().getSchoolType() 
hobbyList[0]                    属性 hobbyList 的第一个元素 
                                索引属性可能是一个数组、列表、
其它天然有序的容器。 
map[key]                        属性 map(java.util.Map 类型) 
                                map 中 key 对应的值 








自定义属性编辑器进行数据绑定


模型对象
/**
 * 用于测试属性编辑器
 * 
 * @version
 * 
 * @Description:
 * 
 * @author <a href="mailto:zhenhuayue@sina.com">Retacn</a>
 * 
 * @since 2014-7-28
 * 
 */
public class DataBinderTestModel {
/* 用户名 */
private String username;
/* boolean值测试 */
private boolean bool;
/* 学校信息 */
private SchoolInfoModel schoolInfo;
/* 集合测试 */
private List hobbyList;
/* map测试 */
private Map map;
/* 电话号码 */
private PhoneNumberModel phoneNumber;
/* 日期类型测试 */
private Date date;
/* string-->enum类型转换 */
private UserState state;


public String getUsername() {
return username;
}


public void setUsername(String username) {
this.username = username;
}


public boolean isBool() {
return bool;
}


public void setBool(boolean bool) {
this.bool = bool;
}


public SchoolInfoModel getSchoolInfo() {
return schoolInfo;
}


public void setSchoolInfo(SchoolInfoModel schoolInfo) {
this.schoolInfo = schoolInfo;
}


public List getHobbyList() {
return hobbyList;
}


public void setHobbyList(List hobbyList) {
this.hobbyList = hobbyList;
}


public Map getMap() {
return map;
}


public void setMap(Map map) {
this.map = map;
}


public PhoneNumberModel getPhoneNumber() {
return phoneNumber;
}


public void setPhoneNumber(PhoneNumberModel phoneNumber) {
this.phoneNumber = phoneNumber;
}


public Date getDate() {
return date;
}


public void setDate(Date date) {
this.date = date;
}


public UserState getState() {
return state;
}


public void setState(UserState state) {
this.state = state;
}


}




public class PhoneNumberModel {
/* 区号 */
private String areaCode;
/* 电话号码 */
private String phoneNumber;


public String getAreaCode() {
return areaCode;
}


public void setAreaCode(String areaCode) {
this.areaCode = areaCode;
}


public String getPhoneNumber() {
return phoneNumber;
}


public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}


}






属性编辑器
/**
 * phoneNumber属性编辑器
 * 
 * @version
 * 
 * @Description:
 * 
 * @author <a href="mailto:zhenhuayue@sina.com">Retacn</a>
 * 
 * @since 2014-7-28
 * 
 */
public class PhoneNumberEdit extends PropertyEditorSupport {
Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$");


@Override
public String getAsText() {
PhoneNumberModel phoneNumber = (PhoneNumberModel) getValue();
return null == phoneNumber ? "" : phoneNumber.getAreaCode() + "-" + phoneNumber.getPhoneNumber();
}


@Override
public void setAsText(String text) throws IllegalArgumentException {
// 如果值为空
if (null == text || !StringUtils.hasLength(text)) {
setValue(null);
}
Matcher matcher = pattern.matcher(text);
if (matcher.matches()) {
PhoneNumberModel phoneNumber = new PhoneNumberModel();
phoneNumber.setAreaCode(matcher.group(1));
phoneNumber.setPhoneNumber(matcher.group(2));
setValue(phoneNumber);
} else {
throw new IllegalArgumentException(String.format("类型转换失败,需要格式[010-12345678]", text));
}
}
}




控制器DataBinderTestController 
public class DataBinderTestController extends AbstractCommandController {


/**
* 构造器
*/
public DataBinderTestController() {
// 设置命令对象
setCommandClass(DataBinderTestModel.class);
// 设置命令对象的名字
setCommandName("dataBinderTest");
}


@Override
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws Exception {
System.out.println("command" + command);
return new ModelAndView("bindAndValidate/success").addObject("dataBinderTest", command);
}


@Override
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
super.initBinder(request, binder);
// 注册自定义属性编辑器
// 注册日期类型属性编辑器
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CustomDateEditor dateEditor = new CustomDateEditor(df, true);
binder.registerCustomEditor(Date.class, dateEditor);


// 注册电话号码编辑器
binder.registerCustomEditor(PhoneNumberModel.class, new PhoneNumberEdit());


}


}


sping配置文件


<!-- 用于测试属性编辑器 -->
<bean name="/dataBind" class="cn.yue.mvc.controller.DataBinderTestController" />






页面视图F/jsp/bindAndValidate/success.jsp
<%@ page language="java" pageEncoding="UTF-8"
contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page isELIgnored="false"%>
<!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>success</title>
</head>
<body>
EL phoneNumber:${dataBinderTest.phoneNumber}
<br /> EL state:${dataBinderTest.state}
<br /> EL date:${dataBinderTest.date}
<br />
</body>
</html>


测试请求路径
http://localhost:8089/dataBind?username=yue&bool=yes&schooInfo.specialty=computer&hobbyList[0]%20=program&hobbyList[1]=music&map[key1]=value1&map[key2]=value2&phoneNumber=010-12345678&date=2014-7-28%2016:48:48&state=blocked




注册propertyEditor


/**
 * 批量注册propertyEditor
 * 
 * @version
 * 
 * @Description:
 * 
 * @author <a href="mailto:zhenhuayue@sina.com">Retacn</a>
 * 
 * @since 2014-7-28
 * 
 */
public class MyWebBindingInitializer implements WebBindingInitializer {


@Override
public void initBinder(WebDataBinder binder, WebRequest request) {
// 注册自定义属性编辑器
// 注册日期类型属性编辑器
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CustomDateEditor dateEditor = new CustomDateEditor(df, true);
binder.registerCustomEditor(Date.class, dateEditor);


// 注册电话号码编辑器
binder.registerCustomEditor(PhoneNumberModel.class, new PhoneNumberEdit());


}
}


DataBinderTestController,注释掉 initBinder 方法


修改spring配置文件
<!--批量注册属性编辑器 -->
<!-- 注册WebBindingInitializer实现 -->
<bean id="myWebBindingInitializer"
class="cn.yue.mvc.controller.support.initializer.MyWebBindingInitializer" />
<bean name="/dataBind" class="cn.yue.mvc.controller.DataBinderTestController">
<!-- 注入WebBindingInitializer实现 -->
<property name="webBindingInitializer" ref="myWebBindingInitializer" />
</bean>




测试请求路径
http://localhost:8089/dataBind?username=yue&bool=yes&schooInfo.specialty=computer&hobbyList[0]%20=program&hobbyList[1]=music&map[key1]=value1&map[key2]=value2&phoneNumber=010-12345678&date=2014-7-28%2016:48:48&state=blocked






***************************************************************************


将我们自定义的 PropertyEditor 放在和你的模型类同包下即可, Editor 命名规则必须是“模型类名 Editor",以PhoneNumberModel为例,PhoneNumberModelEdit可以放在相同目录下,Spring 会自动使用标准 JavaBean 架构进行自动识别


// 注册电话号码编辑器
// binder.registerCustomEditor(PhoneNumberModel.class, new
// PhoneNumberModelEdit());
MyWebBindingInitializer中可以注释掉以下内容
// 注册电话号码编辑器
// binder.registerCustomEditor(PhoneNumberModel.class, new
// PhoneNumberModelEdit());




测试请求路径
http://localhost:8089/dataBind?username=yue&bool=yes&schooInfo.specialty=computer&hobbyList[0]%20=program&hobbyList[1]=music&map[key1]=value1&map[key2]=value2&phoneNumber=010-12345678&date=2014-7-28%2016:48:48&state=blocked




**************************************************************************


数据验证
bindException-->bindingResult-->errors


示例代码如下:




数据绑定失败


控制器
public class ErrorController extends AbstractCommandController {


/**
* 构造器
*/
public ErrorController() {
setCommandClass(DataBinderTestModel.class);
setCommandName("command");
}


@Override
protected ModelAndView handle(HttpServletRequest reque, HttpServletResponse respon, Object command, BindException errors) throws Exception {
// 用户名不能为空
errors.reject("username.not.empty");
// 带有默认错误消息
errors.reject("username.not.empty1", "用户名不能为空1");
// 带有参数和默认值错误消息
errors.reject("username.length.error", new Object[] { 5, 10 }, "用户名长度不正确,必须在5-10之间");


// 得到错误相关的模型数据
Map model = errors.getModel();
return new ModelAndView("bindAndValidate/error", model);
}
}






public class DataBinderErrorTestController extends SimpleFormController {
/**
* 构造器
*/
public DataBinderErrorTestController() {
setCommandClass(DataBinderTestModel.class);
setCommandName("dataBinderTest");
}


@Override
protected ModelAndView showForm(HttpServletRequest request, HttpServletResponse response, BindException errors) throws Exception {
// 如果表单提交有任何错误都会再回到表单展示页
System.out.println(errors);
return super.showForm(request, response, errors);
}


/**
* 表单提交成功进行数据处理
*/
@Override
protected void doSubmitAction(Object command) throws Exception {
System.out.println(command);
super.doSubmitAction(command);
}


/**
* 注册属性编辑器
*/
@Override
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
super.initBinder(request, binder);
// 注册自定义属性编辑器
// 注册日期类型属性编辑器
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CustomDateEditor dateEditor = new CustomDateEditor(df, true);
binder.registerCustomEditor(Date.class, dateEditor);


// 注册电话号码编辑器
binder.registerCustomEditor(PhoneNumberModel.class, new PhoneNumberModelEdit());


}


}














spring配置文件:
<!-- 错误信息:获取错误码对应的错误消息 -->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
<property name="fileEncodings" value="utf-8" />
<property name="cacheSeconds" value="120" />
</bean>


<bean name="/error" class="cn.yue.mvc.controller.ErrorController" />




<!-- 测试数据绑定失败 -->
<bean name="/dataBindError" class="cn.yue.mvc.controller.DataBinderErrorTestController">
<property name="formView" value="bindAndValidate/input" />
<property name="successView" value="bindAndValidate/success" />
</bean>






messages.properties文件
username.not.empty=用户名不能为空 
username.length.error=用户名长度不合法,长度必须在{0}到{1}之间




页面视图/jsp/bindAndValidate/error.jsp
<%@ page language="java" pageEncoding="UTF-8"
contentType="text/html; charset=UTF-8"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!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>success</title>
</head>
<body>
<!-- 表单的默认命令对象名为command -->
<form:form commandName="command">
<form:errors path="*"></form:errors>
</form:form>
</body>
</html>


/jsp/bindAndValidate/input.jsp文件内容如下:


<%@ page language="java" pageEncoding="UTF-8"
contentType="text/html; charset=UTF-8"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!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>success</title>
</head>
<body>
<!-- 表单的命令对象名为dataBinderTest -->
<form:form commandName="dataBinderTest">
<form:errors path="*" cssStyle="color:red"></form:errors>
<br />
<br /> 
bool:<form:input path="bool" />
<br /> 
  phoneNumber:<form:input path="phoneNumber" />
<br /> 
  date:<form:input path="date" />
<br />
<input type="submit" value="提交" />
</form:form>
</body>
</html>




提交表单会提示如下错误 :
**************************************************************************
//错误消息
Failed to convert property value of type java.lang.String to required type boolean for property bool; nested exception is java.lang.IllegalArgumentException: Invalid boolean value [123]
Failed to convert property value of type java.lang.String to required type java.util.Date for property date; nested exception is java.lang.IllegalArgumentException: Could not parse date: Unparseable date: "123"
Failed to convert property value of type java.lang.String to required type cn.yue.mvc.model.PhoneNumberModel for property phoneNumber; nested exception is java.lang.IllegalArgumentException: 类型转换失败,需要格式[010-12345678]


//表单数据
 bool: 123
 phoneNumber:123
 date:123


//控制台错误信息
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 3 errors
Field error in object 'dataBinderTest' on field 'bool': rejected value [123]; codes [typeMismatch.dataBinderTest.bool,typeMismatch.bool,typeMismatch.boolean,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [dataBinderTest.bool,bool]; arguments []; default message [bool]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'boolean' for property 'bool'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value [123]]
Field error in object 'dataBinderTest' on field 'date': rejected value [123]; codes [typeMismatch.dataBinderTest.date,typeMismatch.date,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [dataBinderTest.date,date]; arguments []; default message [date]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'date'; nested exception is java.lang.IllegalArgumentException: Could not parse date: Unparseable date: "123"]
Field error in object 'dataBinderTest' on field 'phoneNumber': rejected value [123]; codes [typeMismatch.dataBinderTest.phoneNumber,typeMismatch.phoneNumber,typeMismatch.cn.yue.mvc.model.PhoneNumberModel,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [dataBinderTest.phoneNumber,phoneNumber]; arguments []; default message [phoneNumber]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'cn.yue.mvc.model.PhoneNumberModel' for property 'phoneNumber'; nested exception is java.lang.IllegalArgumentException: 类型转换失败,需要格式[010-12345678]]




***************************************************************************


数据绑定失败,错误码的查找顺序
1、typeMismatch.命令对象名.属性名 
2、typeMismatch.属性名 
3、typeMismatch.属性全限定类名(包名.类名) 
4、typeMismatch


修改message.propertites文件添加错误消息
typeMismatch.dataBinderTest.date=您输入的数据格式错误,请重新输入(格式:2014-07-299 10:32:17)


重接提交表单看到如下消息:


Failed to convert property value of type java.lang.String to required type boolean for property bool; nested exception is java.lang.IllegalArgumentException: Invalid boolean value [123]
//此处为添加的错误消息内容
您输入的数据格式错误,请重新输入(格式:2014-07-299 10:32:17)
Failed to convert property value of type java.lang.String to required type cn.yue.mvc.model.PhoneNumberModel for property phoneNumber; nested exception is java.lang.IllegalArgumentException: 类型转换失败,需要格式[010-12345678]


 bool:123
 phoneNumber:123
 date:123




数据不合法:
验证不合法数据的两种格式:
a 编程式验证器验证
b 声明式验证


编程式验证器


public class UserModelValidator implements Validator {
/* 用户名正则 */
private static final Pattern USERNAME_PATTERN = Pattern.compile("[a-zA-Z]\\w{4,19}");
/* 用户密码正则 */
private static final Pattern PASSWORD_PATTERN = Pattern.compile("[a-zA-Z0-9]{5,20}");
/* 用户名屏蔽字 */
private static final Set<String> FORBINDDDEN_WORD_SET = new HashSet<String>();
static {
FORBINDDDEN_WORD_SET.add("fuck");
FORBINDDDEN_WORD_SET.add("admin");
}


@Override
public boolean supports(Class<?> clazz) {
// 只对userModel类型的目标实施验证
return UserModel.class == clazz;
}


/**
* 验证的具体方法
*/
@Override
public void validate(Object target, Errors errors) {
// 检查用户名属性是否为空
ValidationUtils.rejectIfEmpty(errors, "username", "username.not.empty");
UserModel user = (UserModel) target;
// 检查用户名是否合法
if (!USERNAME_PATTERN.matcher(user.getUsername()).matches()) {
errors.rejectValue("username", "username.not.illegal");
}
for (String forbiddenWord : FORBINDDDEN_WORD_SET) {
if (user.getUsername().contains(forbiddenWord)) {
errors.rejectValue("username", "username.forbidden", new Object[] { forbiddenWord }, "您的用户名包含非法关键字");
break;
}
}
// 检查用户密码
if (!PASSWORD_PATTERN.matcher(user.getPassword()).matches()) {
errors.rejectValue("password", "password.not.illegal", "密码不合法");
}
}
}








spring配置文件添加如下:
<!-- 编码实现数据合法性验证 -->
<bean id="userModelValidator"
class="cn.yue.mvc.controller.support.validator.UserModelValidator" />
<bean name="/validator" class="cn.yue.mvc.controller.RegisterSimpleFormController">
<property name="formView" value="registerAndValidator" />
<property name="successView" value="redirect:/success" />
<property name="validator" ref="userModelValidator" />
</bean>




message.propertites文件添加如下内容:
username.not.illegal=用户名错误,必须以字母开头,只能出现字母、数字、下划线,并且长度在5-20之间 
username.forbidden=用户名中包含非法关键词【{0}】 
password.not.illegal=密码长度必须在 5-20 之间






页面视图:/jsp/registerAndValidator.jsp


<%@ page language="java" pageEncoding="UTF-8"
contentType="text/html; charset=UTF-8"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>success</title>
</head>
<body>
<form:form commandName="user">


<form:errors path="*" cssStyle="color:red" />
<br /> 
 
username:<form:input path="username" />
<form:errors path="username" cssStyle="color:red" />
<br /> 
 
password:<form:password path="password" />
<form:errors path="password" cssStyle="color:red" />
<br />
<input type="submit" value="注册" />
</form:form>
</body>
</html>






测试:http://localhost:8089/validator


用户名中包含非法关键词【shit】 
密码长度必须在 5-20 之间
username:shit123 用户名中包含非法关键词【shit】 
password: 密码长度必须在 5-20 之间




^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
在multiactionController控制器中,没有方法提供error对象
创建errors对象,添加到模型中即可




BindException errors = new BindException(user, getCommandName(user)); 
//如果用户名为空 
if(!StringUtils.hasLength(user.getUsername())) { 
 //注入错误码
 errors.rejectValue("username", "username.not.empty"); 

//如果有错误就返回新增页面,显示错误消息
if(errors.hasErrors()) { 
 return new ModelAndView(getCreateView()).addAllObjects(errors.getModel()); 



^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
声明式验证器 jsr-303验证框架

基于controll接口的实现不能使用该方式,需要以注解的方式才可以


参考:http://jinnianshilongnian.iteye.com/blog/1752171 


posted @ 2014-08-06 14:00  retacn_yue  阅读(253)  评论(0编辑  收藏  举报