今天开第四章啦,预计本系列教程(Java Web框架)将于12月前完成哈,共勉,加油↖(^ω^)↗!
本章总结:
1 :Struts2 有哪几种类型转换器?
boolean和Boolean:完成String和布尔型之间的转换。
char和Character:完成String和字符型之间的转换。
int和Integer:完成String和整型之间的转换。
long和Long:完成String和长整型之间的转换。
float和Float:完成String和单精度浮点型之间的转换。
double和Double:完成String和双精度浮点型之间的转换。
Date:完成String和日期类型之间的转换,日期格式为用户请求本地的SHORT格式。
数组:该类型在数据转换时,必须满足需要转换的数据中每一个元素都能转换成数组的类型。但若程序员自定义类型转换器,则要根据情况判断。
集合:在使用集合类型转换器时,如果集合中的数据无法确定,可以先将其封装到一个String类型的集合中,然后在用到某个元素时再进行手动转换。
2 :Struts2 输入校验的几种方法?
在Action类中的处理方法进行校验,若无指定方法就用execute()方法。
继承ActionSupport类,并重写其validate()方法实现校验。
应用Struts 2的校验框架进行校验。
第一部分 Struts 2内置类型转换器
4.1.1 一个默认类型转换器的实例
就利用以前的例子编写Action和JavaBean类,注册Action,编写JSP即可:
建立一个简单的页面,如图所示,让客户端填写用户信息,然后提交,在另一个页面上显示出所填信息:
运行该程序,提交后出现如图所示的界面,可见自动将用户输入的生日转换为本地的localdate格式了:
第二部分 自定义类型转换器
4.2.1 继承DefaultTypeConverter类实现转换器
本小节实例需要实现的功能:在左图的输入栏输入一个正确完整的电话后,单击【提交】按钮,出现如右图所示的界面,分别输出区号和电话号。
大致过程:
1.创建Struts 2项目
建立项目,取项目名为“MyTypeConverter”。加载Struts 2包及修改web.xml文件,配置Struts 2,操作同前篇文章,不再赘述。
2.构造模型传值
本例依然采用模型传值,构造模型Tel类,Tel.java实现为:
package org.vo;
public class Tel {
private String sectionNo; //区号
private String telNo; //电话号
public String getSectionNo() {
return sectionNo;
}
public void setSectionNo(String sectionNo) {
this.sectionNo = sectionNo;
}
public String getTelNo() {
return telNo;
}
public void setTelNo(String telNo) {
this.telNo = telNo;
}
}
对应converter2.jsp页面的代码写为:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title>自定义类型转换器</title>
</head>
<body>
<s:form action="typeconverter2" method="post">
<s:textfield name="tel" label="请输入电话"></s:textfield>
<s:submit value="提交"></s:submit>
</s:form>
</body>
</html>
3.编写控制器Action
编写Action类“MyTypeConverterAction”的代码为:
package org.action;
import org.vo.Tel;
import com.opensymphony.xwork2.ActionSupport;
public class MyTypeConverterAction extends ActionSupport{
private Tel tel;
public String execute() throws Exception {
return SUCCESS;
}
public Tel getTel() {
return tel;
}
public void setTel(Tel tel) {
this.tel = tel;
}
}
在struts.xml文件中配置action:
<action name="typeconverter2" class="org.action.MyTypeConverterAction">
<result name="success">/show2.jsp</result>
</action>
4.实现类型转换器
自定义类型转换器需要实现Struts 2框架提供的TypeConverter接口,但这个接口比较复杂,里面的convertValue方法参数太多,不容易实现,Struts 2框架于是提供了继承该接口的实现类DefaultTypeConverter。该类中有两个convertValue方法,其中第2个参数比较多的是TypeConverter类中的方法,所以继承该方法后,只需重写第1个convertValue方法即可。
故本例中自定义类型转换器MyTypeConverter类可写为:
package org.converter;
import java.util.Map;
import org.vo.Tel;
import ognl.DefaultTypeConverter;
public class MyTypeConverter extends DefaultTypeConverter{
public Object convertValue(Map context, Object value, Class toType) {
//如果是从字符串向Tel类型转换
if(toType==Tel.class){
//把参数value转换为字符串数组
String[] str = (String[])value;
Tel t=new Tel();
//把传的字符串循环按“-”分割
for(int i=0;i<str.length;i++){
if(str[i].indexOf("-")>0){
//取出字符串数组中第一个值,然后按“-”分割得到字符串数组
String [] tels=str[0].split("-");
//分别把得来的值赋予Tel的属性
t.setSectionNo(tels[0]);
t.setTelNo(tels[1]);
}else
return null;
}
return t;
}else if(toType == String.class){ //如果是从对象转换为字符串
Tel t=(Tel)value;
return "["+t.getSectionNo()+"-"+t.getTelNo()+"]"; //返回字符串即可
}
return null;
}
}
5.注册类型转换器
这里要把提交的字符串类型转换为Action类中的tel属性的Tel类型,故在该Action类所在路径下编写一个配置文件即可。该配置文件名称必须遵守“action类名-conversion.properties”格式,所以本例的配置文件名为:“MyTypeConverterAction-conversion. properties”,并且同Action放在同一位置(这里是org.action包)下。
在MyTypeConverterAction-conversion.properties文件中写入如下注册信息:
tel=org.converter.MyTypeConverter
配置文件内容为“变量名=包名.类名”。其中,“变量名”是Action类中转换的属性名(本例为tel),“包名.类名”就是要用的自定义类型转换器所在的包及其类名。
6.编写JSP
最后编写显示页面show2.jsp,代码如下:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<title>自定义类型转换</title>
</head>
<body>
<s:property value="tel"/><br>
区号:<s:property value="tel.sectionNo"/><br>
号码:<s:property value="tel.telNo"/>
</body>
</html>
4.2.2 继承StrutsTypeConverter类实现转换器
我们回顾下上小节的转换过程:首先,页面传递的数据被Struts 2拦截,根据struts.xml中的配置找到MyTypeConverterAction类,然后在系统环境中寻找是否含有该Action类对应的MyTypeConverterAction-conversion. properties配置文件,如果找到,将其触发,对变量tel进行类型转换。完成后,执行Action类中的execute方法,最后跳转到show2.jsp页面。
可以看出,自定义的类型转换器继承了DefaultTypeConverter类,并重写了其convertValue方法,里面用了一些if…else语句来判断是从String向对象类型转换,还是从对象向String类型转换(实际上,类型转换也就两个方向:当接收数据时是从String向目标类型转换,而输出时则是向String类型转换),这样难免有点麻烦,Struts 2框架还提供了一个StrutsTypeConverter类,该类继承了DefaultTypeConverter类。
该类已经实现了convertValue方法,并把对不同方向(String到目标类型还是目标类型到String)的处理分成两个方法来处理,程序员只需重写这两个对应的方法即可(convertFromString和convertToString),比之继承DefaultTypeConverter更为简单。故可以把【实例4.1】的自定义转换器用继承该类来实现:
package org.converter;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;
import org.vo.Tel;
public class MyStrutsTypeConverter extends StrutsTypeConverter{
public Object convertFromString(Map arg0, String[] arg1, Class arg2) {
Tel t=new Tel();
String [] str=arg1[0].split("-");
t.setSectionNo(str[0]);
t.setTelNo(str[1]);
return t;
}
public String convertToString(Map arg0, Object arg1) {
Tel t=(Tel)arg1;
return "["+t.getSectionNo()+"-"+t.getTelNo()+"]";
}
}
要对该转换器进行注册,需要修改配置文件的内容为:
tel=org.converter.MyStrutsTypeConverter
前面配置的就属于局部类型转换器,全局类型转换器就是让整个系统关于某个类型的转换都可以应用配置的类型转换器,全局类型转换器的配置非常简单,只需改变配置文件的名称、内容及位置即可。
在上例中如果要配置全局类型转换器,只需在“src”下建立配置文件“xwork-conversion.properties”(与struts.xml文件在同一目录下)即可,内容编写为:
org.vo.Tel=org.converter.MyStrutsTypeConverter
就是都带上包名和全类名即可,若同时有局部和全局类型转换器配置文件存在,则局部优先级大于全局。(大部分约定都遵守这个规定)
注:以上代码有可能出现以下错误:
No result defined for action org.action.MyTypeConverterAction and result input
或者
ognl.NoConversionPossible
此上错误是由于种种原因造成的,现在不过多赘述,如果有读者遇到此类型问题,可以在公众号留言,我们一起探讨↖(^ω^)↗。
第三部分 数组和集合类型的转换
4.3.1 数组类型转换器
假如现在有这样一个页面,该页面要求用户输入两个电话号码,然后提交后分别显示其区号及号码。页面如图所示:
这个时候就需要用数组来传值了,如Action须修改:
同时类型转换器也需要稍作修改:
运行后,结果如图所示:
4.3.2 集合类型转换器
集合类型和数组类型差不多,也需要使用局部类型转换器,可以在【实例4.3】基础上稍做修改转换为集合类型。首先Action类修改为:
其他就是类型转换器实现类了:
第四部分 Struts 2输入校验
Struts 2应用中的输入校验大致有下面几种方法:
【实例4.4】开发一个简单的注册功能项目,然后分别用不同的校验方法对用户填写的注册信息进行校验。
创建Struts 2项目,名为ValidateTest,然后加载Struts 2包,配置web.xml文件,操作步骤同【实例2.1】的第1步、第2步,不再赘述。
首先是注册页面index.jsp:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<title>注册页面</title>
</head>
<body>
<s:form action="regist" method="post">
<s:textfield name="username" label="姓名"></s:textfield>
<s:password name="password" label="密码"></s:password>
<s:password name="repassword" label="确认密码"></s:password>
<s:textfield name="age" label="年龄"></s:textfield>
<s:submit value="提交"></s:submit>
</s:form>
</body>
</html>
Action类文件RegistAction.java为,后续有改动:
package org.action;
import com.opensymphony.xwork2.ActionSupport;
public class RegistAction extends ActionSupport{
private String username; //姓名
private String password; //密码
private String repassword; //确认密码
private int age; //年龄
//上述属性的set和get方法
public String execute() throws Exception {
return SUCCESS;
}
}
在struts.xml文件中配置为,后续也有改动:
<action name="regist" class="org.action.RegistAction">
<result name="success">/success.jsp</result>
</action>
注册成功页面success.jsp为:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<title>成功页面</title>
</head>
<body>
恭喜您<s:property value="username"/>!您已经注册成功
</body>
</html>
4.4.1 使用execute()方法校验
这是最简单的办法,只要在RegistAction类的execute()方法中添加校验代码即可,如下:
public String execute() throws Exception {
if(username.equals("")||username==null){
addFieldError("username","username为空");
}else if(password.equals("")|| password==null){
addFieldError("password","password为空");
}else if(repassword.equals("")||repassword==null){
addFieldError("repassword","repassword为空");
}else if(!password.equals(repassword)){
addFieldError("password","两次输入密码不同");
}else if(age<1||age>150){
addFieldError("age","age必须在1到150之间");
}
if(hasErrors()){
return INPUT;
}
return SUCCESS;
}
相应地在struts.xml中也要加入验证失败后的跳转页面:
<result name="input">/index.jsp</result>
运行后,不输入任何值直接单击【提交】按钮,会出现如下图所示的界面:
若输入“username”的值再单击【提交】按钮,则出现“password为空”的错误提示信息,如下图所示:
4.4.2 重写validate()方法校验
自定义Action类在继承了ActionSupport后可以重写其validate()方法来实现输入校验,当在Action类中定义了该方法后,该方法会在执行系统的execute()方法之前执行。如果执行该方法之后,Action类的fieldErrors中已经包含了数据校验错误信息,将把请求转发到input逻辑视图处。直接把execute()方法中的代码移动到validate()方法中即可:
4.4.3 使用Struts 2校验框架
前两种方法虽然实现简单,但是校验方法都写在Action方法中,代码臃肿,耦合度太高,不利于项目的维护与管理,此时可以使用Struts 2校验框架。在与Action同目录下,创建一个配置文件:ActionName-validation.xml。如上个例子就应该为:RegistAction-validation.xml。另外,该文件也可以叫做:ActionName-别名-validation.xml,别名就是action配置的name值,上个例子也可以名字为:RegistAction-regist-validation.xml。
1.使用<validate></validate>标签
例如,对username字段进行非空校验,应配置为:
2.使用<field></field>标签
例如,对usename字段进行非空校验,应配置为:
Struts 2提供了下面几种类型的校验框架:
required:检查字段是否为空。
requiredstring:检查字段是否为字符串且是否为空。
int:检查字段是否为整数且在[min,max]范围内。
double:检查字段是否为双精度浮点数且在[min,max]范围内。
date:检查字段是否为日期格式且在[min,max]范围内。
expression:对指定OGNL表达式求值。
fieldexpression:对指定OGNL表达式求值。
email:检查字段是否为E-mail格式。
stringlength:检查字符串长度是否在指定范围内。
regex:检查字段是否匹配指定的正则表达式。
根据上述介绍,配置【实例4.4】校验文件RegistAction-validation.xml,注意,此时Action中的validate()方法记得删掉,代码为:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="username">
<field-validator type="requiredstring">
<!-- 去空格 -->
<param name="trim">true</param>
<message>username为空!</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>password为空!</message>
</field-validator>
</field>
<field name="repassword">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>repassword为空!</message>
</field-validator>
<field-validator type="fieldexpression">
<param name="expression">
<![CDATA[(repassword.equals(password))]]>
</param>
<message>两次输入密码不同!</message>
</field-validator>
</field>
<field name="age">
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>age必须在1到150之间</message>
</field-validator>
</field>
</validators>
这样就可以完成同前面类似的校验。运行程序,输入password及repassword,但让两次输入的密码不同,就会出现如下图所示的验证失败界面:
4.4.4 客户端校验
前面讲的都是在服务器上进行校验的,只不过这样校验一是反应时间较长,影响客户体验,二是总是提交到服务器上校验,也浪费服务器资源。好在Struts2框架也提供了客户端校验,但是有局限性,校验器类型较少,只能实现一部分的数据校验。不过现在主流的前端框架也都提供了数据校验,大家可以留心下。
现在我们在上个实例的基础上进行修改:
(1)在注册页面的<s:form>中加入“validate="true" ”。
(2)在项目WebRootWEB-INF下创建content文件夹,然后将项目的所有JSP页面都移到该文件夹下。
(3)在struts.xml中增加配置如下:
<action name="*">
<result>/WEB-INF/content/{1}.jsp</result>
</action>
(4)部署运行程序,在IE地址栏输入“http://localhost:9080/ValidateTest/index”并回车,不输入任何内容直接单击“提交”按钮,结果会如图所示的验证失败界面:
Struts2框架会把配置的验证信息转为JavaScript代码,从而实现前端验证:
但是大家可以发现,Struts2自身的校验,没有两次输入密码不同的校验,只支持以下几种校验:
好了,本章内容到此结束,下章我们会讲Struts2应用进阶--Struts2拦截器。敬请期待!
本章所有实例代码的GitEE地址:
https://gitee.com/jahero/bookManage.git