Struts2(上)
一、 經典的MVC模式
二、 Struts1.x對MVC的實現
三、 Struts1.x的主要組件和作用
組件 |
作用 |
ActionServlet |
中央控制器 |
Action |
分控制器,調用JavaBean實現業務邏輯,Action可以分爲Action、DispatchAction等不同類型 |
ActionForm |
封裝參數,實現服務器驗證,文件上傳等 |
Forward |
轉發到目的地,可以是JSP,也可以是另一個Action |
Struts-config.xml |
配置文件,配置有ActionForm,Action,Forward等,通過XML解析,然後使用反射技術使用該文件 |
Struts標籤庫 |
和JSTL類似,和Struts-config.xml、ActionForm、Action等對象可以緊密集成 |
四、 Struts1.x 開發實例-登錄程序
見案例。地方的
五、 Struts2(WebWork)的MVC
Struts2的MVC架構
六、 Struts2的主要組件
組件 |
作用 |
FilterDispatcher |
起中央控制器作用的過濾器 |
Action |
處於Model層的Action,調用JavaBean實現業務邏輯 |
struts.xml |
核心配置文件,配置有Action、Result等 |
result |
和forward類似,轉發的目的地,支持多種視圖技術。 |
七、 Struts2的實例-登錄
在MyEclipse環境中建立一個新的web工程,名稱“Struts2Login”,存放路徑“f:\Struts2Login”。
在Struts2.0.11版本中找到war包struts2-blank-2.0.11.war,解開該war包,在WEB-INF/lib目錄下複製出所有的jar包,這些jar包就是一個Struts2程序所需要的基礎jar包,把它們複製到新建的web工程的WEB-INF/lib目錄中。
在src目錄下建立一個類,包名mypack,類名UserAction,其代碼如下:
package mypack; import com.opensymphony.xwork2.ActionSupport; public class UserAction extends ActionSupport { private String username; private String userpass; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUserpass() { return userpass; } public void setUserpass(String userpass) { this.userpass = userpass; } @Override public String execute() throws Exception { if ("Mike".equals(username) && "123".equals(userpass) || "張三".equals(username) && "abc".equals(userpass)) return "success"; else return "error"; } } |
在src目錄下建立Struts2的配置文件struts.xml,內容如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- 定義包管理配置的action 繼承struts-default.xml中的配置 --> <package name="actions" extends="struts-default"> <!-- 定義Action(login.action) --> <action name="login" class="mypack.UserAction"> <!-- 定義轉發路徑對應的字符串名 --> <result name="success">/Success.jsp</result> <result name="error">/Error.jsp</result> </action> </package> </struts> |
修改web.xml,在其中加入一個過濾器,過濾/*路徑。
<!-- 過濾器類 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter> <!-- 過濾所有的url請求 --> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
在webroot目錄下編寫3個jsp頁面,index.jsp、Success.jsp和Error.jsp,注意這裏把page指令中的pageEncoding設置爲“utf-8”,就沒有中文問題了。
l index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> </head> <body> <!-- 提交到action --> <form action="login.action" method="post"> 用戶名: <!-- 參數名和action中的屬性名一樣 --> <input type=text name=username> <br> 密 碼: <input type=password name=userpass> <br> <input type=submit name=subm value="提交"> <input type=reset name=reset value="取消"> </form> </body> </html> |
l Success.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'Success.jsp' starting page</title> </head> <body> <h1> 歡迎 <%=request.getParameter("username")%> ,登錄 </h1> </body> </html> |
l Error.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'Error.jsp' starting page</title> </head> <body> <h1> 用戶名或密碼錯誤! </h1> </body> </html> |
把程序部署在tomcat5.5以上的版本中運行。
圖1-2 index.jsp
圖1-3 Success.jsp
八、 Struts2的中文亂碼解決
中文亂碼問題一般是指當請求參數有中文時,無法在Action中得到正確的中文。Struts2中有2種辦法可以解決這個問題:
l 設置JSP頁面的pageEncoding=”utf-8”,就不會出現中文亂碼;
l 如果JSP頁面的pageEncoding=”GBK”,那麼需要修改struts.i18n.encoding=GBK,在struts.xml中加入如下語句進行修改。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.i18n.encoding" value="GBK"/> …… </struts> |
上面2種方法可以解決POST請求中的中文參數,但是GET請求中的中文參數不能解決,GET請求中的中文參數的亂碼需要通過修改Tomcat的server.xml文件來解決,修改如下內容,加入URIEncoding=”GBK”:
<Connector port="8080" …… URIEncoding="GBK"/> |
九、 Struts2的Action中訪問web對象
Struts2的Action就是一個普通的POJO對象,它和Web對象request、response、session和application沒有耦合在一起,這樣便於單獨測試Action,那麼我們在Action中如何訪問這些web對象呢?
訪問這些web內部對象有2種方式:
l 直接訪問Web對象
Struts2框架提供org.apache.struts2.ServletActionContext輔助類來獲得web對象。
HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); HttpSession session = request.getSession(); ServletContext application = ServletActionContext.getServletContext(); |
l Action訪問ActionContext
com.opensymphony.xwork2.ActionContext是一個Action執行的上下文,Action執行期間所用到的對象都保存在ActionContext中,例如session、參數等,並且ActionContext是一個局部線程變量,不用擔心Action的線程安全。
ActionContext context = ActionContext.getContext(); |
該類的常用方法見表1-3所示:
表1-3 ActionContext中的常用方法
Object get(Object key) |
使用key來查找當前ActionContext中的值 |
Map getApplication() |
返回一個Application範圍的Map |
static ActionContext getContext() |
獲得當前線程的ActionContext實例 |
Map getParameters() |
Map類型的所有HttpServletRequest的參數 |
Map getSession() |
Map類型的HttpSession值 |
ValueStack getValueStack() |
返回一個ValueStack類型OGNL值棧 |
void put(Object key,Object value) |
向當前ActionContext存入值,等於在HttpServletRequest中加入值 |
void setApplication(Map application) |
設置application上下文 |
void setSession(Map session) |
設置session值,參數爲Map實例 |
這種方法使用的所有對象和Web對象沒有直接聯繫,所以在測試的時候也是很方便的,我們推薦在程序中使用此方法來訪問web對象。
十、 操作實例 Struts2實現按類別查詢圖書
本案例的工程文件結構如圖1-7所示,文件說明見表1-4所示:
圖1-7 案例的工程文件結構
表1-4 案例的文件說明
文件名 |
說明 |
BookAction.java |
Strut2的Action |
DbConn.java |
數據庫連接類 |
BookOper.java |
數據庫查詢類 |
Book.java |
數據對象類 |
struts.xml |
Struts2的action配置文件 |
web.xml |
Web應用配置文件,Struts2框架在其中配置了Filter |
booklist.jsp |
顯示查詢結果頁面 |
Index.jsp |
輸入書籍類別的頁面 |
1. 生成一個web project,名稱爲“Struts2按類別查詢圖書”,指定存放路徑爲f:\ Struts2按類別查詢圖書;
2. 生成Model層的JavaBean;
l 數據庫連接類DbConn.java
package db; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DbConn { public static Connection getConn() { Connection conn = null; try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); conn = DriverManager.getConnection( "jdbc:sqlserver://localhost:1433;databasename=pubs", "sa", ""); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } } |
l 數據庫查詢類BookOper.java
package oper; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import po.Book; public class BookOper { public List<Book> selectByType(String type) { Connection conn = db.DbConn.getConn(); java.sql.PreparedStatement pstmt = null; java.sql.ResultSet rs = null; List<Book> data = new ArrayList<Book>(); try { pstmt = conn .prepareStatement("select title_id,title,type,price from titles where type=?"); pstmt.setString(1, type); rs = pstmt.executeQuery(); while (rs.next()) { Book book = new Book(); book.setTitleid(rs.getString(1)); book.setTitle(rs.getString(2)); book.setType(rs.getString(3)); book.setPrice(rs.getFloat(4)); data.add(book); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (rs != null) try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (pstmt != null) try { pstmt.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (conn != null) try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return data; } } |
l 數據持久化類Book.java
package po; //圖書的數據類 public class Book { private String titleid; private String title; private String type; private float price; public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getTitleid() { return titleid; } public void setTitleid(String titleid) { this.titleid = titleid; } public String getType() { return type; } public void setType(String type) { this.type = type; } } |
3. 生成Struts2的Action對象,在src目錄中編寫action的配置文件struts.xml;
l BookAction.java
package action; import oper.BookOper; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class BookAction extends ActionSupport { private String type; //書類型 private BookOper bo = new BookOper(); //查詢類 public String getType() { return type; } public void setType(String type) { this.type = type; } @Override public String execute() throws Exception { // TODO Auto-generated method stub //查詢結果放在request中 ActionContext.getContext().put("booklist", bo.selectByType(type)); //返回success指定的頁面 return SUCCESS; //"success" } } |
l struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="actions" extends="struts-default"> <action name="typequery" class="action.BookAction"> <!-- name="success" --> <result>/booklist.jsp</result> </action> </package> </struts> |
4. 生成JSP頁面index.jsp和booklist.jsp;
l index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> </head> <body> <h1>按類別查詢圖書</h1> <form action="typequery.action" method="post"> 書籍類別:<input type=text name=type> <!-- 參數名和action中屬性名一樣 --> <input type=submit name=subm value="查詢"> </form> </body> </html> |
l booklist.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'booklist.jsp' starting page</title> </head> <body> <h1>查詢結果</h1> <table border=1> <tr><th>編號</th><th>名稱</th><th>類型</th><th>單價</th></tr> <c:forEach var="book" items="${booklist}"> <tr><td>${book.titleid }</td> <td>${book.title }</td> <td>${book.type }</td> <td>${book.price }</td></tr> </c:forEach> </table> </body> </html> |
5. 修改web.xml,在其中配置Struts2的過濾器;
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> |
6. 在tomcat5.5以上版本中部署web程序爲“type”,運行效果如圖1-8和1-9。
圖1-8 index.jsp
圖1-9 booklist.jsp
Struts2(下)
1. 繼承ActionSupport實現Action
通過繼承ActionSupport來實現Action是我們的推薦做法,因爲ActionSupport中提供了輸入驗證、國際化、execute等常用方法,使得編寫Action時代碼很簡單。
package mypack; import com.opensymphony.xwork2.ActionSupport; public class HelloWorld extends ActionSupport { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String execute() throws Exception { // TODO Auto-generated method stub message = "大家好!"; return SUCCESS; } } |
ActionSupport實現了Action接口,這個接口中定義了一些常量和execute方法。
public abstract interface com.opensymphony.xwork2.Action{ // 定義常量 public static final java.lang.String SUCCESS = "success"; public static final java.lang.String NONE = "none"; public static final java.lang.String ERROR = "error"; public static final java.lang.String INPUT = "input"; public static final java.lang.String LOGIN = "login"; // 定義抽象方法 public abstract java.lang.String execute() throws Exception; } |
ActionSupport類的代碼部分內容如下:
public class com.opensymphony.xwork2.ActionSupport implements com.opensymphony.xwork2.Action,com.opensymphony.xwork2.Validateable, com.opensymphony.xwork2.ValidationAware,com.opensymphony.xwork2.TextProvider, com.opensymphony.xwork2.LocaleProvider,java.io.Serializable{ ...... //添加字段異常 public void addFieldError(java.lang.String fieldName,java.lang.String errorMessage){} //execute public String execute() throws Exception{} //輸入驗證 public void validate(){} ...... } |
struts.xml的配置如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="actions" extends="struts-default"> <action name="hello" class="mypack.HelloWorld"> <result>/Success.jsp</result> </action> </package> </struts> |
2. 模型驅動(ModelDriven)的Action
Struts2的Action屬於MVC模型層,Action中的方法代表業務邏輯,Action中的屬性代表請求中的參數,當頁面請求參數較多的時候,把過多的參數對象的屬性定義在Action中不太符合Struts所倡導的鬆耦合原則,所以我們推薦單獨用JavaBean來封裝參數,在Action中爲JavaBean賦值,這就是ModelDriven的Action。模型驅動的Action要求Action實現ModelDriven接口,假如登錄頁面需要傳輸參數username和userpass,我們把這2個參數封裝在一個數據的JavaBean中,然後在Action中定義該JavaBean爲Model即可,代碼如下:
l Userinfo.java
package po; //用戶名和密碼的封裝對象 public class Userinfo { private String username; private String userpass; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUserpass() { return userpass; } public void setUserpass(String userpass) { this.userpass = userpass; } } |
l UserAction.java
package action; import po.Userinfo; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class UserAction extends ActionSupport implements ModelDriven<Userinfo> { private Userinfo model = new Userinfo(); @Override public String execute() throws Exception { // TODO Auto-generated method stub return SUCCESS; } //返回模型對象的實例 public Userinfo getModel() { // TODO Auto-generated method stub return model; } } |
當請求該Action的時候,請求中的參數會自動填充到模型Userinfo的屬性中,當然需要參數名和屬性名一樣,到跳轉的頁面上利用Struts2標籤<s:property value="username" />可以取出模型Userinfo中的屬性username。在ModelDriven接口中的方法getModel()必須實現,通過它告訴系統模型的具體對象是什麼。
l struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="actions" extends="struts-default"> <action name="user" class="action.UserAction"> <result>/new.jsp</result> </action> </package> </struts> |
l new.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'new.jsp' starting page</title> </head> <body> username:<s:property value="username" /> </body> </html> |
3. 多方法的Action
Action中的方法代表業務邏輯,那麼一個模塊中的多個業務邏輯如何用Action來處理呢?我們有2種辦法來處理這個問題:
1. 一個Action對應一個業務邏輯,實現方便,但是Action數量多,struts.xml中需要配置的內容也多,這種方法不推薦;
2. 一個Action對應多個業務邏輯,例如表的CRUD操作,含有多個業務邏輯,我們只寫一個Action來實現,Action的數量沒有增加,struts.xml的配置也簡單,所以這種方法是我們推薦的做法。
Action中自定義方法的聲明和execute方法一樣,方法的調用路徑爲“Action名稱!方法名稱.action”。
public String 方法名() throws Exception{} |
以用戶表Userinfo的CRUD操作爲例,看一下多方法Action的代碼:
package action; import po.Userinfo; import service.UserService; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class CrudUserAction extends ActionSupport implements ModelDriven<Userinfo> { // crud業務方法 private UserService userservice = new UserService(); private Userinfo userinfo=new Userinfo(); // 模型對象userinfo public Userinfo getModel() { // TODO Auto-generated method stub return userinfo; } // 增加 public String create() throws Exception { userservice.createUser(userinfo); return SUCCESS; } // 查詢 public String retrive() throws Exception { // 查詢結果放在request中 ActionContext.getContext().put("userlist", userservice.selectUsers()); return "list"; } // 修改 public String update() throws Exception { userservice.updateUser(userinfo); return SUCCESS; } // 刪除 public String delete() throws Exception { userservice.deleteUser(userinfo.getUsername()); return SUCCESS; } // 默認的execute public String execute() throws Exception { return SUCCESS; } } |
在struts.xml中配置如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="actions" extends="struts-default"> <action name="CrudUser" class="action.CrudUserAction"> <result>/Success.jsp</result> <result name="list">/UserList.jsp</result> </action> </package> </struts> |
調用CRUD業務邏輯的請求路徑見表2-1。
表2-1 多方法Action中每個方法的請求路徑
業務邏輯方法 |
請求路徑 |
create() |
CrudUser!create.action |
retrive() |
CrudUser!retrive.action |
update() |
CrudUser!update.action |
delete() |
CrudUser!delete.action |
execute() |
CrudUser.action |
二、 Result類型
Action中表示跳轉的目的地使用了在struts.xml配置的字符串,格式爲:<result name=”” type=””></result>,type可以有多種選擇,Struts2支持各種視圖技術,例如JSP、JSF、XML等,默認的是JSP。常見的type類型配置如下:
l dispatcher
轉發到JSP頁面,和<jsp:forward page=””/>的效果一樣,是默認類型。
<result>/Success.jsp</result> <result name=”a”>/Success.jsp</result> <result name=”b” type=”dispatcher”>/Success.jsp</result> |
l redirect
重定向到JSP頁面,和response.sendRedirect(“”)的效果一樣。
<result name=”a” type=”redirect”>/Success.jsp</result> |
l redirect-action
重定向到action,目的地爲Action,配置時可以指定如下兩個參數:actionName-重定向的Action名;namespace-重定向的Action所在的命名空間。
<result name=”a” type=”redirect-action”> <param name=”actionName”>myaction</param> <param name=”namespace”>/test</param> </result> |
l chain
轉發到action,形成action-chain,可以指定兩個參數:actionName-重定向的Action名;namespace-重定向的Action所在的命名空間。
<result type=”chain”> <param name=”actionName”>myaction</param> <param name=”namespace”>/test</param> </result> |
l stream
用於向頁面返回一個InputStream,原始數據直接傳遞給HttpServletResponse,這種結果類型在用戶下載文件(例如PDF文件等)等情況下非常有意義。
<result name=”success” type=”stream”> <param name=”contentType”>image/jpg</param> <param name=”inputName”>imageStream</param> <param name=”contentDisposition”>filename=”document.pdf”</param> <param name=”buffersize”>1024</param> </result> |
l plaintext
用於輸出目的地JSP/HTML的源代碼內容,可以指定兩個參數:location-目的地JSP/HTML,charSet-輸出內容時使用的字符集。
<result name="success" type="plaintext"> <param name="location">/Success.jsp</param> <param name="charset">utf-8</param> </result> |
除了上述類型以外,還支持如下的類型:
l chart:用於整合JFreeChart的result類型;
l freemarker:用於整合FreeMarker的result類型;
l httpheader:用於處理特殊http行爲的result類型;
l jasper:用於整合JasperReport的result類型;
l jsf:用於整合JSF的result類型;
l titles:用於整合Titles的result類型;
l velocity:用於整合Velocity的result類型;
l xslt:用於整合XML/XSLT的result類型。
這些視圖技術的支持,有些還需要導入相應的插件包,即Struts2提供的含有plugin字樣的jar包。
三、 輸入驗證
1. javaScript客戶端驗證
2. validate方法驗證
l 在Action中加入validate驗證方法,把添加和修改時驗證分開,添加用戶時驗證方法爲validateCreate(),修改用戶時的驗證方法爲validateUpdate()。
package action; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.ServletActionContext; import oper.UserOper; import po.Userinfo; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class UserAction extends ActionSupport implements ModelDriven<Userinfo> { // 數據模型 private Userinfo user = new Userinfo(); // 業務類 private UserOper uo = new UserOper(); public Userinfo getModel() { // TODO Auto-generated method stub return user; } // 增加前 public String precreate() throws Exception { return "addupdate"; } // 增加 public String create() throws Exception { uo.create(user); return select(); } // 刪除 public String delete() throws Exception { uo.delete(user.getUserid()); return select(); } // 修改 public String update() throws Exception { uo.update(user); return select(); } // 查詢 public String select() throws Exception { ActionContext.getContext().put("userlist", uo.retriveAll()); return SUCCESS; } // 查詢單個 public String retrive() throws Exception { Userinfo myuser = uo.retriveOne(user.getUserid()); user.setUsername(myuser.getUsername()); user.setUserpass(myuser.getUserpass()); user.setSex(myuser.getSex()); user.setSfz(myuser.getSfz()); user.setBirthday(myuser.getBirthday()); user.setWorktime(myuser.getWorktime()); user.setEmail(myuser.getEmail()); user.setInterest(myuser.getInterest()); user.setIntro(myuser.getIntro()); user.setXl(myuser.getXl()); return "addupdate"; } // 驗證方法 // 增加時驗證 public void validateCreate() { checkForm(); // 用戶名是否存在 if(uo.checkUserName(user.getUsername())) this.addFieldError("username", "用戶名已經被佔用"); } // 修改時驗證 public void validateUpdate() { checkForm(); if(uo.checkUserName(user.getUsername(),user.getUserid())) this.addFieldError("username", "用戶名已經被佔用"); } // 驗證要求 public void checkForm() { HttpServletRequest request = ServletActionContext.getRequest(); // 用戶名不能爲空 if (user.getUsername() == null || user.getUsername().equals("")) this.addFieldError("username", "用戶名不能爲空"); // 密碼不能爲空 if (user.getUserpass() == null || user.getUsername().equals("")) this.addFieldError("userpass", "密碼不能爲空"); // 2次密碼相同 else if (!user.getUserpass().equals(request.getParameter("userpass1"))) this.addFieldError("userpass", "2次密碼不一樣"); // 身份證不能爲空 if (user.getSfz() == null || user.getSfz().equals("")) this.addFieldError("sfz", "身份證不能爲空"); // 身份證必須是15或18位 else if (!user.getSfz().matches("^\\d{17}[\\d|X]|\\d{15}$")) this.addFieldError("sfz", "身份證必須是15位或18位"); // email不能爲空 if (user.getEmail() == null || user.getEmail().equals("")) this.addFieldError("email", "Email不能爲空"); else if (!user.getEmail().matches("^\\w+@\\w+(\\.\\w+)+$")) this.addFieldError("email", "Email格式錯誤"); // 生日不能爲空 if (user.getBirthday() == null) this.addFieldError("birthday", "生日不能爲空"); // 興趣至少選一項 if (user.getInterest() == null) this.addFieldError("interest", "興趣至少選一項"); // 工作年限1-100之間 if (user.getWorktime() < 1 || user.getWorktime() > 100) this.addFieldError("worktime", "工作年限必須在1-100之間"); // 簡介不能爲空 if (user.getIntro() == null || user.getIntro().equals("")) this.addFieldError("intro", "簡介不能爲空"); } } |
l Struts.xml中增加了驗證出錯時跳轉到的頁面<result name=”input”></result>。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- 請求參數的中文處理 --> <constant name="struts.i18n.encoding" value="GBK"/> <!-- 修改後的xml自動加載 --> <constant name="struts.configuration.xml.reload" value="true"/> <package name="actions" extends="struts-default"> <action name="user" class="action.UserAction"> <result>/UserList.jsp</result> <result name="addupdate">/AddUpdate.jsp</result> <result name="input">/AddUpdate.jsp</result> </action> </package> </struts> |
3. validate框架(xml)驗證
爲了使用Struts2的框架驗證文件進行輸入驗證,需要建立一個特定的驗證規則文件,該文件是一個XML格式配置文件,文件命名規則爲<Action類名-validation.xml>,保存在Action實現類相同的目錄下,如果是多方法Action,需要使用Action的別名配置(每個方法映射爲不同的Action別名,可以使用通配符),每個方法的驗證文件名爲<Action類名-別名-validation.xml>。這個例子中的驗證文件的名字爲:StuAction-validation.xml。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <!-- 驗證規則定義根元素 --> <validators> <!-- 第一個驗證字段:姓名name --> <field name="name"> <!-- 驗證規則:非空(系統預先定義好的規則) --> <field-validator type="requiredstring"> <!-- 錯誤提示 --> <message>姓名不能爲空</message> </field-validator> <!-- 驗證規則:長度在5-10之間(系統預先定義好的規則) --> <field-validator type="stringlength"> <param name="minLength">5</param> <param name="maxLength">10</param> <!-- 錯誤提示 --> <message>姓名長度必須在${minLength}-${maxLength}個字符之間</message> </field-validator> </field> <!-- 第二個驗證字段:年齡age --> <field name="age"> <field-validator type="conversion" short-circuit="true"> <!-- 錯誤提示 --> <message>必須輸入整數</message> </field-validator> <field-validator type="int"> <param name="min">20</param> <param name="max">35</param> <!-- 錯誤提示 --> <message>年齡必須在${min}-${max}之間</message> </field-validator> </field> <!-- 第三個驗證字段:分數mark --> <field name="mark"> <field-validator type="conversion" short-circuit="true"> <!-- 錯誤提示 --> <message>必須輸入數字</message> </field-validator> <field-validator type="double"> <param name="minInclusive">50</param> <param name="maxInclusive">100</param> <!-- 錯誤提示 --> <message>分數必須在${minInclusive}-${maxInclusive}之間</message> </field-validator> </field> <!-- 第三個驗證字段:入學時間enrolltime --> <field name="enrolltime"> <field-validator type="conversion" short-circuit="true"> <!-- 錯誤提示 --> <message>必須是日期格式</message> </field-validator> <field-validator type="required"> <!-- 錯誤提示 --> <message>入學時間不能爲空</message> </field-validator> <field-validator type="date"> <param name="min">1990-01-01</param> <param name="max">2008-10-09</param> <!-- 錯誤提示 --> <message>入學時間必須在${min}到${max}之間</message> </field-validator> </field> </validators> |
上述驗證文件中,short-circuit屬性爲true表示發生驗證錯誤的時候,後續驗證不再執行,即“短路”。
Action的局部資源文件StuAction.properties做如下修改,這樣當數據類型轉換異常的時候由驗證框架文件來給出錯誤提示,而基本標籤的提示指定爲空,不然會出現重複提示:
invalid.fieldvalue.age= invalid.fieldvalue.mark= invalid.fieldvalue.enrolltime= |
重新運行原來的例子,顯示驗證錯誤結果信息如圖2-9所示:
圖2-9 使用驗證框架文件的輸入驗證
四、 Strut2的國際化
l 在struts.xml中配置struts.custom.i18n.resources常量
<constant name="struts.custom.i18n.resources" value="globalMessages"/> |
l 在src目錄下建立中文和英文的資源文件,中文資源文件globalMessages_zh_CN.properties的內容如下:
username=用戶名 userpass=密碼 success=登錄成功 error=登錄失敗 login=登錄 |
使用native2ascii工具把該文件轉換爲unicode編碼。
l 英文資源文件globalMessages_en.properties的內容如下:
username=User Name userpass=Password success=Welcome error=Sorry! You can not log in login=Login |
l 編寫登錄頁面Login.jsp,其內容如下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'Login.jsp' starting page</title> </head> <body> <s:form action="dologin"> <s:textfield name="username" key="username"/> <s:password name="userpass" key="userpass"/> <s:submit key="login"/> </s:form> </body> </html> |
登錄成功頁面Success.jsp內容如下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'Success.jsp' starting page</title> </head> <body> <s:property value="username"/> <s:text name="success"/> </body> </html> |
登錄失敗頁面Error.jsp內容如下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'Error.jsp' starting page</title> </head> <body> <s:text name="error"></s:text> </body> </html> |
上述頁面中的Struts2標籤都可以訪問資源文件,表單標籤<s:textfield name=”” key=””/>
中的屬性key用於訪問資源文件,<s:text name=””/>標籤中的name屬性用於訪問資源文件。
五、 標籤庫和OGNL
在jsp頁面中使用標籤庫指令引入後<% taglib uri=”/struts-tags” prefix=”s” %>,就可以使用所有的Struts2標籤了。
六、 Struts2攔截器
攔截器(interceptor)是Struts2框架核心組成部分。很多功能都是構建在攔截器基礎之上的,例如文件的上傳和下載、國際化、數據類型轉換和數據有效性驗證等,Struts2利用內建的攔截器,完成了框架內的大部分操作。
攔截器就是動態攔截Action調用的對象。它提供了一種機制,使開發者可以定義一個特定的功能模塊,這個模塊可以在Action執行之前或之後運行,也可以在一個Action執行之前阻止Action執行。同時也提供了一種可以提取Action中可重用部分的方式。
Struts2框架的Action被一個或者多個攔截器(攔截器棧)所包圍,所有的用戶請求都會被攔截器所攔截,然後交給Action處理,處理結果以邏輯視圖的方式返回給用戶。而這個調用的執行流程,是由Strut2的配置文件(struts.xml)來實現的。
在前面幾章中,沒有明確說明攔截器,爲什麼可以直接調用Action呢?那是因爲在Struts2框架中如果沒有顯式的攔截器配置,則系統會調用默認的攔截器來調用Action,在用戶看來,好像沒有配置攔截器一樣。演示一個簡單攔截器的開發:HelloWorld攔截器。
假設我們需要實現這麼一個功能,在調用每個Action之前都能在控制檯打印出“HelloWorld”。這樣的一個功能使用Struts2攔截器來實現最簡單。下面介紹一個具體的實現步驟。
1. 建立一個Action類MyAction.java和配置文件Struts.xml;
l MyAction.java
package action; import com.opensymphony.xwork2.ActionSupport; public class MyAction extends ActionSupport { private int age;//年齡 public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String execute() throws Exception { // TODO Auto-generated method stub System.out.println("Action execute......"); return SUCCESS; } } |
l struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="action" extends="struts-default"> <action name="test" class="action.MyAction"> <result>Success.jsp</result> </action> </package> </struts> |
2. 建立一個攔截器類HelloWorldInterceptor.java,其代碼如下;
package interceptor; import action.MyAction; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class HelloWorldInterceptor extends AbstractInterceptor { //攔截方法 @Override public String intercept(ActionInvocation arg0) throws Exception { // 獲得被調用的Action類 Object action = arg0.getAction(); //打印HelloWorld System.out.println("攔截器信息:HelloWorld!"); //執行Action或調用下一個攔截器 String result = arg0.invoke(); //執行完action後提示 System.out.println("Action執行完畢!"); return result; } } |
3. 在struts.xml中加入攔截器的配置,見struts.xml內容;
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="action" extends="struts-default"> <!-- 定義攔截器 --> <interceptors> <interceptor name="helloworld" class="interceptor.HelloWorldInterceptor"/> </interceptors> <action name="test" class="action.MyAction"> <result>Success.jsp</result> <!-- action中引用攔截器 --> <interceptor-ref name="helloworld"/> </action> </package> </struts> |
4. 編寫JSP頁面Success.jsp。
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'Success.jsp' starting page</title> </head> <body> <h1>調用成功</h1> <s:property value="age"/> </body> </html> |
5. 運行該web程序,在地址欄輸入http://localhost:8080/hw/test.action?age=35,運行效果如圖4-1到4-2所示。
圖4-1 執行Action後的輸出
圖4-2 interceptor運行結果
從圖4-1我們可以看到,程序的運行有錯誤,就是參數age的值沒有填充到Action的屬性age中,所以頁面上顯示age就是0,那麼這是爲什麼呢?是因爲參數的值填充到名稱相同的屬性中這個過程也是依賴攔截器實現的,但是我們一旦在action中引用了自己定義的攔截器,那麼系統默認的攔截器就沒有了,所以我們只要在struts.xml的配置中讓Action引用默認攔截器就正確了,默認攔截器是通過<package … extends=”struts-default”></package>中的extends引入的,修改後的struts.xml如下。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="action" extends="struts-default"> <!-- 定義攔截器 --> <interceptors> <interceptor name="helloworld" class="interceptor.HelloWorldInterceptor" /> </interceptors> <action name="test" class="action.MyAction"> <result>Success.jsp</result> <!-- action中引用默認攔截器 --> <interceptor-ref name="defaultStack" /> <!-- action中引用攔截器 --> <interceptor-ref name="helloworld" /> </action> </package> </struts> |
6. 再次運行程序,輸出結果正確,見圖4-3所示。
圖4-3 加入默認攔截器後的執行結果
七、 攔截器應用實例-文件上傳和下載
Struts2框架默認使用Common-fileUpload組件實現文件上傳,該組件將解析出HttpServletRequest請求中的文件域信息,並使用IO流方式,將文件保存在服務器的指定位置。
Struts2框架中本身不帶有Common-fileUpload組件的jar包,需要自己下載後放在web程序的WEB-INF/lib目錄中,需要2個jar包,一個是commons-fileupload-1.2.1.jar,可從網址http://jakarta.apache.org/commons/fileupload下載,另一個是commons-io-1.4.jar,可從網址http://jakarta.apache.org/commons/io/。
l 文件上傳
上傳單個文件的JSP頁面代碼如下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> </head> <body> <s:form action="fileupload" method="post" enctype="multipart/form-data"> 上傳文件:<s:file name="doc"/><br> <s:submit value="上傳"/> </s:form> </body> </html> |
form表單的enctype屬性設置爲multipart/form-data。enctype用來指定表單數據的編碼方式,有如下3個值。
1. application/x-www-form-urlencoded:指定該值,則表單中的數據被編碼爲Key-Value對,這是默認的編碼方式。
2. multipart/form-data:使用mime編碼,會以二進制流的方式來處理表單數據,文件上傳需要使用該編碼方式。
3. text/plain:表單數據以純文本形式進行編碼,其中不含任何控件和格式字符。
file類型表單域doc用於選擇上傳文件,它和Action中的java.io.File類型的屬性doc對應,同時上傳文件的文件名對應於Action中的屬性docFileName,上傳文件的文件類型對應於Action中的屬性docContentType。一般說來,爲了上傳文件,如果表單域名稱爲xxx,那麼在Action中應建立如下3個屬性來接收上傳文件的信息。
private java.io.File xxx;//封裝上傳文件的二進制內容 private String xxxContentType;//封裝上傳文件的文件類型 private String xxxFileName;//封裝上傳文件的文件名 |
封裝上傳文件的數據類FileInfo.java代碼如下:
package po; import java.io.File; public class FileInfo { private File doc; //封裝上傳文件的屬性 private String docFileName; //封裝上傳文件的名稱屬性 private String docContentType; //封裝上傳文件的類型屬性 private String targetdir; //保存路徑 private String targetfilename; //保存的文件名 public File getDoc() { return doc; } public void setDoc(File doc) { this.doc = doc; } public String getDocContentType() { return docContentType; } public void setDocContentType(String docContentType) { this.docContentType = docContentType; } public String getDocFileName() { return docFileName; } public void setDocFileName(String docFileName) { this.docFileName = docFileName; } public String getTargetdir() { return targetdir; } public void setTargetdir(String targetdir) { this.targetdir = targetdir; } public String getTargetfilename() { return targetfilename; } public void setTargetfilename(String targetfilename) { this.targetfilename = targetfilename; } } |
用於文件上傳的Action代碼如下:
package action; import java.io.File; import org.apache.commons.io.FileUtils; import org.apache.struts2.ServletActionContext; import po.FileInfo; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class FileUploadAction extends ActionSupport implements ModelDriven<FileInfo> { // 封裝文件信息 FileInfo fileinfo = new FileInfo(); public FileInfo getModel() { // TODO Auto-generated method stub return fileinfo; } @Override public String execute() throws Exception { // TODO Auto-generated method stub // 獲得服務器上保存上傳文件的目錄updfile的絕對路徑 String realpath = ServletActionContext.getServletContext().getRealPath( "/updfile"); // 設置保存文件的目錄 fileinfo.setTargetdir(realpath); // 設置目標文件名 fileinfo.setTargetfilename(generateFileName(fileinfo.getDocFileName())); // 把doc內容複製到target FileUtils.copyFile(fileinfo.getDoc(), new File(fileinfo.getTargetdir(), fileinfo.getTargetfilename())); return SUCCESS; } // 產生唯一的文件名 private synchronized String generateFileName(String filename) { int position = filename.lastIndexOf("."); String ext = filename.substring(position); return System.nanoTime() + ext; } } |
Action的配置文件struts.xml。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="action" extends="struts-default"> <action name="fileupload" class="action.FileUploadAction"> <result>Success.jsp</result> </action> </package> </struts> |
顯示結果頁面Success.jsp。
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'Success.jsp' starting page</title> </head> <body> 上傳文件類型:<s:property value="docContentType"/><br> 上傳成功後文件位置:<s:property value="targetdir"/><br> 上傳圖片:<img width=200 height=200 src="<s:property value="'updfile/'+targetfilename"/>"><br> </body> </html> |
部署後運行,效果如圖4-4、圖4-5所示。
圖4-4 選擇文件上傳
圖4-5 圖片上傳後顯示
l 上傳文件的過濾
上傳文件的時候可以限制上傳文件的類型和大小,使用攔截器來實現。文件上傳的攔截器是系統攔截器,我們只要配置參數就可以了。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- 上傳文件的臨時保存目錄 --> <constant name="struts.multipart.saveDir" value="/tmp" /> <package name="action" extends="struts-default"> <action name="fileupload" class="action.FileUploadAction"> <result>/Success.jsp</result> <result name="input">/index.jsp</result> <interceptor-ref name="fileUpload"> <!-- 允許上傳的文件類型 --> <param name="allowedTypes"> image/bmp,image/png,image/gif,image/jpeg </param> <!--上傳文件的最大容量 單位字節 --> <param name="maximumSize">20000</param> </interceptor-ref> <interceptor-ref name="defaultStack" /> </action> </package> </struts> |
重新運行該程序,選擇一個比較大的文件,單擊“上傳”按鈕,結果如圖4-6所示。
圖4-6 文件太大
圖4-7 文件類型不對
錯誤提示都是英文,如果要改成中文提示,需要配置全局資源文件globalMessage_zh_CN.proeprties,內容如下:
struts.messages.error.file.too.large=上傳文件太大 struts.messages.error.content.type.not.allowed=上傳文件必須是圖像文件 struts.messages.error.uploading=上傳過程出現異常 |
轉換編碼成爲unicode編碼。然後在struts.xml中引入該資源文件<constant name="struts.custom.i18n.resources" value="globalMessage" />。
重新運行後,效果如圖4-8和4-9所示。
圖4-8 提示文件類型不對
圖4-9 提示上傳文件太大
在struts.xml中配置<constant name="struts.multipart.saveDir" value="/tmp" />用於指定上傳文件保存的臨時目錄,上傳完成後系統會自動刪除臨時目錄中的內容。
l 文件下載
文件下載可以通過配置struts.xml中result的類型來實現,執行下載操作的Action代碼如下所示:
package action; import java.io.InputStream; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class FileDownloadAction extends ActionSupport { private String inputpath; //下載文件路徑 private String contenttype; //文件類型 private String filename; //文件名 //返回一個InputStream類型 public java.io.InputStream getInputStream() { return ServletActionContext.getServletContext().getResourceAsStream(inputpath); } @Override public String execute() throws Exception { //調用相關業務邏輯方法 動態設置下載信息 inputpath = "/updfile/Bliss.jpg"; contenttype = "image/jpeg"; //解決下載的中文文件名問題 filename = java.net.URLEncoder.encode("文件.jpg","utf-8"); return SUCCESS; } public String getContenttype() { return contenttype; } public void setContenttype(String contenttype) { this.contenttype = contenttype; } public String getFilename() { return filename; } public void setFilename(String filename) { this.filename = filename; } public String getInputpath() { return inputpath; } public void setInputpath(String inputpath) { this.inputpath = inputpath; } } |
上述代碼中的java.io.InputStream getInputStream()方法必須有,它返回要下載文件的二進制輸入流。
struts.xml中配置下載Action的result類型爲stream,內容如下:
<action name="filedownload" class="action.FileDownloadAction"> <result name="success" type="stream"> <!-- 定義相關參數 --> <param name="contentType">${contenttype}</param> <param name="inputName">inputStream</param> <param name="bufferSize">4096</param> <param name="contentDisposition">attachment;filename=${filename}</param> </result> </action> |
地址欄輸入http://localhost:8080/upd/filedownload.action,出現如圖4-10所示畫面。
八、 操作案例:驗證框架實現CRUD
使用驗證框架後,增加了一些新的文件,工程文件結構如圖2-19所示。
圖2-19 加入驗證框架後的工程結構
表2-5 案例3增加的文件說明
文件名 |
說明 |
UserAction-usercreate-validation.xml |
調用Action的create()方法時執行的驗證框架文件 |
UserAction-userupdate-validation.xml |
調用Action的update()方法時執行的驗證框架文件 |
UserAction.properties |
Action局部資源文件,用於屏蔽標籤自帶的提示信息 |
Userinfo-myContext-validation.xml |
Visitor驗證類型需要的獨立類的驗證框架文件 |
UsernameUnique.java |
自定義的驗證類 |
validators.xml |
加入自定義的驗證類後驗證類描述文件 |
1. 修改UserAction.java文件,添加模型類的set/get方法,去掉案例2中添加的validate方法;
package action; import oper.UserOper; import po.Userinfo; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; public class UserAction extends ActionSupport implements ModelDriven<Userinfo>{ // 數據模型 private Userinfo user=new Userinfo(); // 業務類 private UserOper uo = new UserOper(); public Userinfo getModel() { // TODO Auto-generated method stub return user; } // 增加前 public String precreate() throws Exception { return "addupdate"; } // 增加 public String create() throws Exception { uo.create(user); return select(); } // 刪除 public String delete() throws Exception { uo.delete(user.getUserid()); return select(); } // 修改 public String update() throws Exception { uo.update(user); return select(); } // 查詢 public String select() throws Exception { ActionContext.getContext().put("userlist", uo.retriveAll()); return SUCCESS; } // 查詢單個 public String retrive() throws Exception { Userinfo myuser = uo.retriveOne(user.getUserid()); user.setUsername(myuser.getUsername()); user.setUserpass(myuser.getUserpass()); user.setSex(myuser.getSex()); user.setSfz(myuser.getSfz()); user.setBirthday(myuser.getBirthday()); user.setWorktime(myuser.getWorktime()); user.setEmail(myuser.getEmail()); user.setInterest(myuser.getInterest()); user.setIntro(myuser.getIntro()); user.setXl(myuser.getXl()); return "addupdate"; } public Userinfo getUser() { return user; } public void setUser(Userinfo user) { this.user = user; } } |
2. 用別名通配符來配置Action的調用路徑,修改struts2.xml爲如下內容;
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- 請求參數的中文處理 --> <constant name="struts.i18n.encoding" value="GBK"/> <!-- 修改後的xml自動加載 --> <constant name="struts.configuration.xml.reload" value="true"/> <package name="actions" extends="struts-default"> <action name="user*" class="action.UserAction" method="{1}"> <result>/UserList.jsp</result> <result name="addupdate">/AddUpdate.jsp</result> <result name="input">/AddUpdate.jsp</result> </action> </package> </struts> |
3. 爲create和update方法編寫驗證框架文件,命名方式爲:<Action類名>-<別名>-validation.xml,和Action類放在同一個目錄下;
l UserAction-usercreate-validation.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <!-- 驗證規則定義根元素 --> <validators> <field name="user"> <field-validator type="visitor"> <!-- 關聯文件帶有的名稱 --> <param name="context">myContext</param> <!-- 表單字段的屬性不用帶字段名的前綴 --> <param name="appendPrefix">false</param> <message></message> </field-validator> </field> </validators> |
l UserAction-userupdate-validation.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <!-- 驗證規則定義根元素 --> <validators> <field name="user"> <field-validator type="visitor"> <!-- 關聯文件帶有的名稱 --> <param name="context">myContext</param> <!-- 表單字段的屬性不用帶字段名的前綴 --> <param name="appendPrefix">false</param> <message></message> </field-validator> </field> </validators> |
4. 編寫模型類的驗證框架文件,命名方式:<模型類名>-<context名>-validation.xml,這裏的名稱就是Userinfo-myContext-validation.xml,要和模型類放在同一目錄中。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <!-- 驗證規則定義根元素 --> <validators> <field name="username"> <field-validator type="requiredstring" short-circuit="true"> <message>用戶名不能爲空</message> </field-validator> <!-- 自定義的驗證規則 用於判斷用戶名是否已經被佔用 --> <field-validator type="usernameunique"> <message>用戶名已經被佔用</message> </field-validator> </field> <field name="userpass"> <field-validator type="requiredstring" short-circuit="true"> <message>密碼不能爲空</message> </field-validator> <field-validator type="fieldexpression"> <param name="expression"> <![CDATA[userpass==userpass1]]> </param> <message>2次輸入密碼不同</message> </field-validator> </field> <field name="sfz"> <field-validator type="requiredstring"> <message>身份證不能爲空</message> </field-validator> <field-validator type="regex"> <param name="expression"> <![CDATA[\d{15}|\d{17}[\d|X]]]> </param> <message>身份證必須是15或18位</message> </field-validator> </field> <field name="birthday"> <field-validator type="conversion" short-circuit="true"> <message>生日格式錯誤</message> </field-validator> <field-validator type="required"> <message>生日不能爲空</message> </field-validator> </field> <field name="email"> <field-validator type="requiredstring"> <message>email不能爲空</message> </field-validator> <field-validator type="regex"> <param name="expression"> <![CDATA[\w+@\w+(\.\w+)+]]> </param> <message>email格式錯誤</message> </field-validator> </field> <field name="worktime"> <field-validator type="conversion" short-circuit="true"> <message>必須是整數</message> </field-validator> <field-validator type="int"> <param name="min">1</param> <param name="max">100</param> <message>必須是${min}-${max}的整數</message> </field-validator> </field> <field name="intro"> <field-validator type="requiredstring"> <message>簡介不能爲空</message> </field-validator> </field> </validators> |
5. 編寫自定義的驗證類,用於驗證用戶名是否被佔用;
package validator; import oper.UserOper; import po.Userinfo; import com.opensymphony.xwork2.validator.FieldValidator; import com.opensymphony.xwork2.validator.ValidationException; import com.opensymphony.xwork2.validator.ValidatorContext; public class UsernameUnique implements FieldValidator { private String fieldName; private String defaultMessage; private ValidatorContext validatorContext; private String validatorType; private String message; private String messageKey; public String getFieldName() { return this.fieldName; } public void setFieldName(String arg0) { this.fieldName = arg0; } public String getDefaultMessage() { return this.defaultMessage; } public String getMessage(Object arg0) { return this.message; } public String getMessageKey() { return this.messageKey; } public ValidatorContext getValidatorContext() { return this.validatorContext; } public String getValidatorType() { return this.validatorType; } public void setDefaultMessage(String arg0) { this.defaultMessage = arg0; } public void setMessageKey(String arg0) { this.messageKey = arg0; } public void setValidatorContext(ValidatorContext arg0) { this.validatorContext = arg0; } public void setValidatorType(String arg0) { this.validatorType = arg0; } public void validate(Object arg0) throws ValidationException { // 得到用戶名和編號 int userid = ((Userinfo) arg0).getUserid(); String username = ((Userinfo) arg0).getUsername(); // 驗證是否存在 UserOper uo = new UserOper(); boolean b = false; if (userid > 0) b = uo.checkUserName(username, userid); else b = uo.checkUserName(username); //存在 加入FieldError if (b) this.validatorContext.addFieldError(this.fieldName, this.defaultMessage); } } |
6. 把自定義的驗證類添加到默認的驗證類文件中,文件名validators.xml,放在src目錄中。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator Config 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd"> <!-- START SNIPPET: validators-default --> <validators> <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/> <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/> <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/> <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/> <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/> <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/> <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/> <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/> <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/> <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/> <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/> <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/> <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/> <validator name="usernameunique" class="validator.UsernameUnique"/> </validators> <!-- END SNIPPET: validators-default --> |
7. 部署運行。