Struts2中Action從表單取值並且存到Web元素中(session) ...
聲明:本博客非原創,【轉載:http://blog.csdn.net/Cece_2012/article/details/7617775】
在struts2中,Action不同於struts1.x中的Action。在struts2中Action並不需要繼承任何控制器類型或實現相應介面。比如struts1.x中的Action需要繼承Action或者DispatcherAction。
同時struts2中的Action並不需要藉助於像struts1.x中的ActionForm獲取表單的數據。可以直接通過與表單元素相同名稱的數據成員(需要存在符合命名規範的set和get方法)獲取頁面表單數據。
雖然struts2中的Action原則上不用繼承任何類型。但是一般需要實現com.opensymphony.xwork2.Action介面或者繼承com.opensymphony.xwork2.ActionSupport類型,然後重寫execute方法。通常更願意去繼承ActionSupport類型,這樣我們可以在我們的控制器中增加更多的功能。因為ActionSupport本身不但實現了Action介面,而且實現了其他的幾個介面,讓我們的控制器的功能更加強大,例如:
com.opensymphony.xwork2.Validateable:提供了validate方法,可以對action中的數據進行校驗
com.opensymphony.xwork2.ValidationAware:提供了addFieldError方法,可以存取action級別或者欄位級別的錯誤消息
com.opensymphony.xwork2.TextProvider:提供了獲取本地化信息文本的方法getText
com.opensymphony.xwork2.LocaleProvider:提供了getLocale方法,用於獲取本地信息
從以上我們可以看到,繼承ActionSupport,可以完成更多的工作。
例如上面的例子,例如我們需要判斷輸入文本框的內容,輸入的內容長度必須在6-10之間。那麼我們可以增加校驗工作,利用validate方法。更改後的代碼如下:
1 package com.frank.action; 2 3 import com.opensymphony.xwork2.ActionSupport; 4 public class HelloWorldAction extends ActionSupport { 5 private String message; 6 private String username; 7 @Override 8 public String execute() throws Exception { 9 this.message="Hello World:"+this.username; 10 return SUCCESS; 11 } 12 public String getUsername() { 13 return username; 14 } 15 public void setUsername(String username) { 16 this.username = username; 17 } 18 public String getMessage() { 19 return message; 20 } 21 public void setMessage(String message) { 22 this.message = message; 23 } 24 @Override 25 public void validate() { 26 if(this.username.trim().length()<6||this.username.trim().length()>10){ 27 addFieldError("user.username","the length is invalid"); 28 } 29 } 30 }View Code
由於實現了validate方法,這樣當請求一個控制器(在這裡為helloWorld.action)的時候,首先執行validate方法,如果有錯誤信息增加到action中,那麼就不繼續執行Action,返回INPUT,否則就繼續執行Action。在本例中,首先判斷用戶名稱是否在合法的長度範圍,如果不在增加錯誤信息,返回INPUT(預設返回)。
因為有錯誤返回INPUT,所以此Action在配置文件中應該定義INPUT轉發路徑
<action name="helloWorld" class="com.frank.action.HelloWorldAction"> <result name="success">display.jsp</result> <result name="input">helloWorld.jsp</result> </action>
出現錯誤時,返回到helloWorld.jsp,併在此頁面中顯示控制器中所註冊的錯誤消息,更改後的頁面如下(黑色字體為增加的)
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'helloWorld.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <h1><s:fielderror/></h1><br> <form action="helloWorld.action"> username:<s:textfield name="username"/> <br> <s:submit align="left"/> </form> </body> </html>View Code
當前Action執行的核心方法都是execute,有時我們可以能希望同一個控制器做不同的工作。例如在對一個表分別進行CRUD操作時,我們可能需要根據不同的情況執行4種操作,這樣一個execute做起來比較麻煩,我們可以定義我們自己的方法,然後控制器有選擇的執行。類似struts1.x中的DispatcherAction控制器一樣。
這時我們自定義的操作方法,必須和execute具有相同的簽名,只是名稱不同而已。例如在前面的例子中我們增加一個超鏈接,讓它同樣請求helloWorld.action,但是在控制器端執行自己定義的方法myExecute。註意在這裡先去掉驗證方法validate,取消驗證。修改後代碼如下:
package com.frank.action; import com.opensymphony.xwork2.ActionSupport; public class HelloWorldAction extends ActionSupport { private String message; private String username; @Override public String execute() throws Exception { this.message="Hello World:"+this.username; return SUCCESS; } public String myExecute() throws Exception{ this.message="Good Morning !!!"; return SUCCESS; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }View Code
此時我們需要增加新的Action配置,並指明調用的方法
<action name="otherHelloWorld" class="com.frank.action.HelloWorldAction" method="myExecute"> <result name="success">display.jsp</result> </action>
在helloWorld.jsp中增加超鏈接,強求新增加的Action
<a href="otherHelloWorld.action">OtherAction</a>
新的請求頁面:
點擊超鏈接後,請求新的Action,結果如下:
由於請求的新Action在配置中指明瞭所調用的方法為myExecute,所以在控制器端不再執行execute方法,轉而執行myExecute,所以輸出結果為
Good Morning !!!
同樣存在另外一種形式請求新的Action:
即:Action名!新的方法名.action,剛纔的超鏈接可以更改成:<a href="helloWorld!myExecute.action">OtherAction</a>
表單數據的提交
表單數據的提交在Web編程中是非常重要的。控制器需要獲取用戶在表示層(頁面)所提交的數據,然後進行下一步操作。比如在用戶登錄操作中,控制器需要獲取用戶在頁面中輸入的“用戶名稱”和“密碼”數據,來決定下一步的驗證。
獲取表單提交的數據有兩種方式:
第一種為使用中介對象(我通常把它稱為包裝表示層數據的包裹)。比如在strtus1.x中使用各種ActionForm來封裝頁面數據。使用中介對象可以同時在中介對象的裡面增加諸如:驗證、過濾、日誌記錄等附加工作。但是同樣帶來了類數量的膨脹,存在各種各樣的ActionForm類(LoginForm、PersonForm等等),不利於項目的維護管理。
第二種是直接使用控制器中的域對象,即直接使用控制器中的數據成員獲取表示層的數據,在struts2種支持此種方式。但必須保證相應的數據成員和表示層提交的數據名稱一致,並且具有符合命名規範的setter和getter方法。這樣可以達到“內省”。直接使用域對象可以減少類的膨脹,如果只是簡單的獲取提交的數據則建議直接使用域對象。
在此我們還是同一個非常簡單的(我經常採用的)登錄操作來體會開發過程。可以通過此操作擴展到複雜的操作,其實原理一樣。還是從頭開始(省去準備工作的步驟,直接進入開發過程):
首先創建一個登錄頁面login.jsp,代碼內容如下:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'login.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <h1><s:fielderror/></h1> <h1>User Login</h1><br> <form action="login.action"> Username:<s:textfield name="username"/><br> Password:<s:textfield name="password"/><br> <input type="submit" value="Login"/> </form> </body> </html>View Code
<s:fielderror/>用於顯示所註冊的錯誤信息
接著建立Actiion類:LoginAction,在此我們通過域對象獲取表示層提交的數據,也就是在控制器中聲明兩個數據成員username和password來得到表示層的數據,然後在execute方法中進行驗證工作(當然,一般規範開發需要在業務類中進行實際驗證),在此簡便此操作。同時重寫validate方法在Action實際執行前進行驗證工作,具體的驗證方式由自己的業務決定。代碼如下:
package com.frank.action; import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport { private String username; private String password; @Override public String execute() throws Exception { if(this.username.equals("admin")&&this.password.equals("admin")){ return SUCCESS; } return INPUT; } @Override public void validate() { if(this.username==null||this.username.trim().length()<1){ addFieldError("no.username","must input username"); } if(this.password==null||this.password.trim().length()<1){ addFieldError("no.password","must intpu password"); } } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }View Code
新建一個index.jsp頁面,用戶在用戶登錄成功後顯示,內容非常的簡單,就是一條“Login Success!!!”
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <h1>Login Success!!!</h1> </body> </html>View Code
現在可以開始配置Action了,在struts.xml中增加新的Action,配置如下:
<action name="login" class="com.frank.action.LoginAction">
<result>index.jsp</result>
<result name="input">login.jsp</result>
</action>
可以部署和運行了,啟動login.jsp
如果在任意一個文本框中不輸入內容或者輸入空白內容,則返回當前頁面,並錯誤提示
如果輸入的用戶名和密碼錯誤,同樣返回到當前頁面
如果輸入的用戶和密碼正確,則到達index.jsp
註意以上所有的提交方式都為預設GET提交方式
封裝表示層數據到業務類中
在實際的開發過程中,我們可以用業務類對象獲取表示層數據,這樣我們可以在業務類中實現更多的業務方法(比如登錄、註冊)。而由控制器去調用業務類。因為struts2同樣遵循的是MVC模式,所以我們不應該讓控制器去做實際的業務工作,而是調用Model,而這裡同樣用Model獲取表示層的數據。
這樣我們可以新建一個類User,在這裡聲明數據成員username和password,並增加一個驗證方法login,用於驗證登錄。User類的代碼如下:
package com.frank.rule; public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public boolean login(){ if(this.username.equals("admin")&&password.equals("admin")){ return true; } return false; } }View Code
既然我們用User獲取用戶的數據,這樣我們需要在Action增加User類型的數據成員,而去掉原來的username和password,重構的代碼如下:
package com.frank.action; import com.opensymphony.xwork2.ActionSupport; import com.frank.rule.User; public class LoginAction extends ActionSupport { private User user; @Override public String execute() throws Exception { if(user.login()){ return SUCCESS; } return INPUT; } @Override public void validate() { if(user.getUsername()==null||user.getUsername().trim().length()<1){ addFieldError("no.username","must input username"); } if(user.getPassword()==null||user.getPassword().trim().length()<1){ addFieldError("no.password","must intpu password"); } } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }View Code
在這裡頁面的數據不再由控制器的username和password數據成員獲取,轉而有其中的user對象進行封裝。所以應該修改login.jsp的提交表單,修改後的結果如下:
<form action="login.action"> Username:<s:textfield name="user.username"/><br> Password:<s:textfield name="user.password"/><br> <input type="submit" value="Login"/> </form>
註意此時提交表單中的文本框名稱必須為user.username和user.password,這樣所輸入的內容會由控制器的user對象獲取。
這裡可能有一個疑問,就是在控制器中並沒有在任何時候創建User對象,那麼內容是如何獲得的呢?難道不會拋出NullPointerException?此時當請求控制器,並設置user對象的username數據成員時,action依次調用下方法:
user.getUser();
user.setUser(new User());
user.getUser().setUsername("admin");
從上可以看出,首先struts2會嘗試獲取user對象,如果不存在則執行第二步創建一個新對象,然後將內賦給數據成員
重新部署,運行,所得到的效果和前一個相同。
使用Servlet相關對象
在進行Web編程時,很多時候需要使用Servlet相關對象,例如:HttpServletRequest、HttpServletResponse、HttpSession、ServletContext。我們可以將一些信息存放到session中,然後在需要的時候取出。
在struts1.x的Action中,可以通過HttpServletRequest和HttpServletResponse參數來得到Servlet相關對象,進而使用。那麼在struts2種如何獲取?
我們可以使用com.opensymphony.xwork2.ActionContext類來完成上述操作。此類的getContext方法可以得到當前Action的上下文,也就是當前Action所處的容器環境,進而得到相關對象。
同時也可以使用org.apache.struts2.ServletActionContext類獲得,例如:
HttpServletRequest req=ServletActionContext.getRequest();
下麵更改前面的登錄操作,在登錄成功後,將登錄人員的信息保存到session範圍中,在index.jsp頁面中取出,我們可以重構我們的Action,在重構前,首先新增一個BaseAction,作為所有Action的父類,在其中增加方法獲取Servlet相關對象,這樣所有的子類都擁有了相應的方法。代碼如下:
package com.frank.action; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.opensymphony.xwork2.ActionSupport; import org.apache.struts2.ServletActionContext; public class BaseAction extends ActionSupport { public HttpServletRequest request(){ return ServletActionContext.getRequest(); } public HttpServletResponse response(){ return ServletActionContext.getResponse(); } public ServletContext application(){ return ServletActionContext.getServletContext(); } public HttpSession session(){ return ServletActionContext.getRequest().getSession(); } }View Code
然後重構LoginAction,繼承自BaseAction,併在登錄成功後再session範圍保存用戶名信息,以便index.jsp讀取,重構後代碼如下:
package com.frank.action; import com.opensymphony.xwork2.ActionSupport; import com.frank.rule.User; public class LoginAction extends BaseAction { private User user; @Override public String execute() throws Exception { if(user.login()){ this.session().setAttribute("username", user.getUsername()); return SUCCESS; } return INPUT; } @Override public void validate() { if(user.getUsername()==null||user.getUsername().trim().length()<1){ addFieldError("no.username","must input username"); } if(user.getPassword()==null||user.getPassword().trim().length()<1){ addFieldError("no.password","must intpu password"); } } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }View Code
在index.jsp中增加讀取session中保存內容的代碼,如下:
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <h1>Login Success!!!</h1> <br> <h1>Username:<%=session.getAttribute("username") %></h1> </body> </html>View Code