好咧,咱们先做准备工作,导入struts1.2的8个JAR包以及相关.tld文件,在web.xml上配置Struts框架的核心控制器ActionServlet!(如果你使用MyEclipse,这些工作就能够一步到位了。)完毕后,我们就来两个Servlet Filter,一个是Struts的作者Craig McClanahan写的,用来解决乱码问题,另一个是我们写的,用来做I18N应用的中英文切换:
1.SetCharacterEncodingFilter.java
package org.leno.struts.util;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* @author Craig McClanahan
* @version $Revision: 1.5 $ $Date: 2005/03/21 18:08:09 $
*/
public class SetCharacterEncodingFilter implements Filter {
protected String encoding = null;
protected FilterConfig filterConfig = null;
protected boolean ignore = true;
public void destroy() {
this.encoding = null;
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Conditionally select and set the character encoding to be used
if (ignore || (request.getCharacterEncoding() == null)) {
String encoding = selectEncoding(request);
if (encoding != null)
request.setCharacterEncoding(encoding);
}
// Pass control on to the next filter
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding");
String value = filterConfig.getInitParameter("ignore");
if (value == null)
this.ignore = true;
else if (value.equalsIgnoreCase("true"))
this.ignore = true;
else if (value.equalsIgnoreCase("yes"))
this.ignore = true;
else
this.ignore = false;
}
protected String selectEncoding(ServletRequest request) {
return (this.encoding);
}
}
2. LanguageFilter.java
package org.leno.struts.util;
import java.io.IOException;
import java.util.Locale;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.Globals;
public class LanguageFilter implements Filter{
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
HttpServletRequest hReq = (HttpServletRequest) request;
HttpSession session = hReq.getSession();
String language = request.getParameter("language");
if(language!=null)
{
if (language.equals("zh_CN")) {
session.setAttribute(Globals.LOCALE_KEY,
Locale.CHINA);
} else {
session.setAttribute(Globals.LOCALE_KEY,
Locale.US);
}
}
chain.doFilter(request, response);
} catch (ServletException sx) {
filterConfig.getServletContext().log(sx.getMessage());
} catch (IOException iox) {
filterConfig.getServletContext().log(iox.getMessage());
}
}
private FilterConfig filterConfig;
//Handle the passed-in FilterConfig
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
}
注意,Filter是从Servlet2.3规范开始新增的功能,并在Servlet2.4规范中得到增强。它就是一个驻留在服务器端的Web组件,可以截取客户端和资源之间的请求与响应信息,并对这些信息进行过滤。当Web容器接收到一个对资源的请求时,它将判断是否有过滤器与这个资源相关联。如果有,那么容器将把请求交给过滤器进行处理。在过滤器中,你可以改变请求的内容,或者重新设置请求的报头信息,然后再将请求发送给目标资源。当目标资源对请求作出响应时,容器同样会将响应先转发给过滤器,在过滤器中,你可以对响应的内容进行转换,然后再将响应发送到客户端。
写完了后就要在web.xml上做配置,如下红色字体部分:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>3</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>3</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<filter>
<filter-name>SetCharacterEncodingFilter</filter-name>
<filter-class>org.leno.struts.util.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>ignore</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SetCharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>LanguageFilter</filter-name>
<filter-class>org.leno.struts.util.LanguageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LanguageFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
为了使用I18N,我们对于中文和英文分别编写两个同名但不同后缀的消息资源属性文件,注意它们的格式:
美国英语:ApplicationResources_en_US.properties
# Resources for parameter 'org.leno.struts.ApplicationResources'
# Project strutsAjax
hotel.title=Hotel Reservation System
hotel.validate.msgarea=* Required fields
label.arrival.date=Arrival Date
label.depart.date=Departure Date
label.smoking.pref=Smoking Preference
select.smoking=Select One
select.smoking.yes=Smoking
select.smoking.no=Non Smoking
label.specialRequests=Special Requests
label.name=Name
label.telephone=Telephone Number
label.submit=Submit
format.date=MM/DD/YYYY
chinese=Chinese
english=English
reservation.success.title=Hotel Reservation Confirmed
reservation.success.msg=Congratulations! Your reservation is confirmed.
# -- application specific errors
errors.departure.before.arrival=Arrival date must occur before departure date.
errors.invalid.date.format=Invalid date format.
errors.invalid.telephone.format=Invalid telephone format.
errors.reservation.not.available=The requested reservation is not available.
# -- standard errors --
errors.header=<ul>
errors.prefix=<li style="color:red">
errors.suffix=</li>
errors.footer=</ul>
# -- validator --
errors.date={0} is not a valid date. Dates must be in the format {1}.
errors.invalid={0} is invalid.
errors.maxlength={0} can not be greater than {1} characters.
errors.minlength={0} can not be less than {1} characters.
errors.range={0} is not in the range {1} through {2}.
errors.required={0} is required.
errors.byte={0} must be a byte.
errors.double={0} must be an double.
errors.float={0} must be an float.
errors.integer={0} must be an integer.
errors.long={0} must be an long.
errors.short={0} must be an short.
errors.creditcard={0} is not a valid credit card number.
errors.email={0} is an invalid e-mail address.
中国汉语:ApplicationResources_zh_CN.properties
# Resources for parameter 'org.leno.struts.ApplicationResources'
# Project strutsAjax
hotel.title=/u5BBE/u9986/u
hotel.validate.msgarea=* /u5FC5/u586B/u4FE1/u
label.arrival.date=/u5230/u8FBE/u65E5/u
label.depart.date=/u79BB/u
label.smoking.pref=/u5438/u70DF/u9700/u
select.smoking=--/u8BF7/u9009/u62E9--
select.smoking.yes=/u53EF/u4EE5/u5438/u70DF
select.smoking.no=/u7981/u6B62/u5438/u70DF
label.specialRequests=/u7279/u6B
label.name=/u59D3/u540D
label.telephone=/u7535/u8BDD/u
label.submit=/u63D0/u4EA4
format.date=MM/DD/YYYY
chinese=/u4E2D/u6587/u7248
english=/u
reservation.success.title=/u786E/u8BA4/u9884/u8BA2/u
reservation.success.msg=/u606D/u
# -- application specific errors
errors.departure.before.arrival=/u5230/u8FBE/u65E5/u
errors.invalid.date.format=/u65E0/u6548/u7684/u65E5/u
errors.invalid.telephone.format=/u65E0/u6548/u7684/u7535/u8BDD/u
errors.reservation.not.available=/u5BF9/u4E0D/u8D77/uFF
# -- standard errors --
errors.header=<ul>
errors.prefix=<li style="color:red">
errors.suffix=</li>
errors.footer=</ul>
# -- validator --
errors.date={0} /u4E0D/u
errors.invalid={0} is invalid.
errors.maxlength={0} can not be greater than {1} characters.
errors.minlength={0} can not be less than {1} characters.
errors.range={0} is not in the range {1} through {2}.
errors.required={0} /u5FC5/u987B/u586B/u5199.
errors.byte={0} must be a byte.
errors.double={0} must be an double.
errors.float={0} must be an float.
errors.integer={0} must be an integer.
errors.long={0} must be an long.
errors.short={0} must be an short.
errors.creditcard={0} is not a valid credit card number.
errors.email={0} is an invalid e-mail address.
写完了后别忘了在struts-config.xml上配置<message-resources>,接下来看看我们的主页面hotelReservation.jsp,密切注意红色字体部分:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@page import="org.apache.struts.Globals"%>
<%@taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<!DOCTYPE HTML PUBLIC "-//W
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title><bean:message key="hotel.title"/></title>
<script type="text/javascript" src="js/hotelReservation.js"></script>
<script type="text/javascript" src="js/prototype-
</head>
<body>
<logic:equal value="zh" name="<%=Globals.LOCALE_KEY %>" property="language" scope="session">
<html:link action="/reservation?language=en_US">
<bean:message key="english"/></html:link>
<bean:message key="chinese"/>
</logic:equal>
<logic:equal value="en" name="<%=Globals.LOCALE_KEY %>" property="language" scope="session">
<bean:message key="english"/>
<html:link action="/reservation?language=zh_CN">
<bean:message key="chinese"/>
</html:link>
</logic:equal>
<h1><bean:message key="hotel.title"/></h1>
<h3><bean:message key="hotel.validate.msgarea"/></h3>
<div id="errors">
<html:errors/>
</div>
<html:form action="saveReservation.do" method="post"
styleId="reservationForm">
<table border="0">
<tbody>
<tr>
<td>
<label>* <bean:message key="label.arrival.date"/>:</label>
</td>
<td>
<html:text property="arrivalDate"
styleId="arrivalDate" onblur="validateForm();"/>
</td>
</tr>
<tr>
<td>
<label>* <bean:message key="label.depart.date"/>:</label>
</td>
<td>
<html:text property="departDate"
styleId="departDate" onblur="validateForm();"/>
</td>
</tr>
<tr>
<td>
<label>* <bean:message key="label.smoking.pref"/>:</label>
</td>
<td>
<html:select property="smokingPref"
styleId="smokingPref" onblur="validateForm();">
<html:option value=""><bean:message key="select.smoking"/></html:option>
<html:option value="Smoking"><bean:message key="select.smoking.yes"/></html:option>
<html:option value="Non Smoking">
<bean:message key="select.smoking.no"/>
</html:option>
</html:select>
</td>
</tr>
<tr>
<td>
<label><bean:message key="label.specialRequests"/>:</label>
</td>
<td>
<html:textarea property="requests"
styleId="requests" rows="6" cols="50" />
</td>
</tr>
<tr>
<td>
<label>* <bean:message key="label.name"/>:</label>
</td>
<td>
<html:text property="name"
styleId="name" />
</td>
</tr>
<tr>
<td>
<label>* <bean:message key="label.telephone"/>:</label>
</td>
<td>
<html:text property="telephone"
styleId="telephone" />(233-124-5555)
</td>
</tr>
<tr>
<td colspan="2" align="center">
<html:submit><bean:message key="label.submit"/></html:submit>
</td>
</tr>
</tbody>
</table>
</html:form>
</body>
</html>
在我们的主页面上用到了struts的html,logic和bean标签来构建我们的应用,特别是使用了I18N——页面上所有的文本和错误提示都是来自于消息资源文件!还用到了两个JS文件,一个是prototype-
function hasEntry(id) {
return $F(id).length > 0;
}
function isFormReadyForValidation() {
var ready = false;
if(hasEntry("arrivalDate")
&& hasEntry("departDate")
&& $F("smokingPref").length > 0) {
ready = true;
}
return ready;
}
function validateForm() {
var isReady = isFormReadyForValidation();
if(isReady) {
sendFormForValidation();
}
}
function sendFormForValidation() {
var queryString = Form.serialize("reservationForm");
queryString = queryString + "&ts=" + new Date().getTime();
var url = "validateReservation.do";
new Ajax.Request(url, {
asynchronous: true,
method: "get",
parameters: queryString,
onComplete: function(request) {
handleResponse(request.responseText);
}
});
}
function handleResponse(text) {
$("errors").innerHTML = text;
}
我们想要达到的效果是:用户依次填写输入字段,一旦填写好了到达日期,离开日期,吸烟嗜好后,就会向服务器发送一个Ajax请求,确认对于用户的请求是否有可用的房间。到达日期,离开日期,吸烟嗜好的onblur事件处理程序会调用js中的validateForm函数。validateForm函数首先会调用isFormReadyForValidation函数确定用户是否填写了到达日期,离开日期和吸烟嗜好,如果都不为空,那么isFormReadyForValidation函数返回true,这时候validateForm函数就会调用sendFormForValidation函数负责真正发送Ajax请求,它先使用Prototype的Form.serialize方法构造将要被发送到服务器的表单值的查询字符串。然后,它会把当前的时间追加到查询字符串中,确保URL的唯一性,避免浏览器缓存请求。准备好查询字符串后,sendFormForValidation函数使用Prototype的Ajax.Request对象构造并发送Ajax请求。我们使用GET方法把Ajax请求异步发送到validateReservation.do这一URL中,并使用handleResponse函数处理服务器的响应。handleResponse函数会更新位于页面顶部的div元素的innerHTML属性,显示服务器返回的错误信息。
这就是我们需要编写的JavaScript!利用Prototype库,我们只需编写很少量的代码就可以实现需要的功能。现在我们浏览器端的编程以及服务器端的准备工作都做好了,下面我们就把注意力转移到服务器端的Struts上。