摘要
Struts2是在WebWork2基礎發展而來的。和struts1一樣, Struts2也屬於MVC框架。不過有一點大家需要注意的是:儘管Struts2和Struts1在名字上的差別不是很大,但Struts2和struts1在代碼編寫風格上幾乎是不一樣的。那麼既然有了struts1,爲何還要推出struts2。主要是因爲struts2有以下優點:
1 > 在軟件設計上Struts2沒有像struts1那樣跟Servlet API和struts API有着緊密的耦合,Struts2的應用可以不依賴於Servlet API和struts API。 Struts2的這種設計屬於無侵入式設計,而Struts1卻屬於侵入式設計。- public class OrderListAction extends Action {
- public ActionForward execute(ActionMapping mapping, ActionForm form,
- HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- }
- }
3> Strut2提供了類型轉換器,我們可以把特殊的請求參數轉換成需要的類型。在Struts1中,如果我們要實現同樣的功能,就必須向Struts1的底層實現BeanUtil註冊類型轉換器才行。
4> Struts2提供支持多種表現層技術,如:JSP、freeMarker、Velocity等
5> Struts2的輸入校驗可以對指定方法進行校驗,解決了Struts1長久之痛。
6> 提供了全局範圍、包範圍和Action範圍的國際化資源文件管理實現
搭建Struts2開發環境
搭建Struts2環境時,我們一般需要做以下幾個步驟的工作:
1》找到開發Struts2應用需要使用到的jar文件.
2》編寫Struts2的配置文件
3》在web.xml中加入Struts2 MVC框架啓動配置
搭建Struts2開發環境--開發Struts2應用依賴的jar文件
大家可以到http://struts.apache.org/download.cgi#struts2014下載struts-2.x.x-all.zip,目前最新版爲2.1.8。下載完後解壓文件,開發struts2應用需要依賴的jar文件在解壓目錄的lib文件夾下。不同的應用需要的JAR包是不同的。下面給出了開發Struts 2程序最少需要的JAR。
struts2-core-2.x.x.jar :Struts 2框架的核心類庫
xwork-core-2.x.x.jar :XWork類庫,Struts 2在其上構建
ognl-2.6.x.jar :對象圖導航語言(Object Graph Navigation Language),struts2框架通過其讀寫對象的屬性
freemarker-2.3.x.jar :Struts 2的UI標籤的模板使用FreeMarker編寫
commons-logging-1.x.x.jar :ASF出品的日誌包,Struts 2框架使用這個日誌包來支持Log4J和JDK 1.4+的日誌記錄。
commons-fileupload-1.2.1.jar 文件上傳組件,2.1.6版本後必須加入此文件
搭建Struts2開發環境-- Struts2應用的配置文件
Struts2默認的配置文件爲struts.xml ,該文件需要存放在WEB-INF/classes下,該文件的配置模版如下:
- <?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>
- </struts>
搭建Struts2開發環境--Struts2在web中的啓動配置
在struts1.x中, struts框架是通過Servlet啓動的。在struts2中,struts框架是通過Filter啓動的。他在web.xml中的配置如下:
- <filter>
- <filter-name>struts2</filter-name>
- <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
- <!-- 自從Struts 2.1.3以後,下面的FilterDispatcher已經標註爲過時
- <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> -->
- </filter>
- <filter-mapping>
- <filter-name>struts2</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
在StrutsPrepareAndExecuteFilter的init()方法中將會讀取類路徑下默認的配置文件struts.xml完成初始化操作。
注意:struts2讀取到struts.xml的內容後,以javabean形式存放在內存中,以後struts2對用戶的每次請求處理將使用內存中的數據,而不是每次都讀取struts.xml文件
第一個Struts2應用--HelloWorld
在默認的配置文件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="itcast" namespace="/test" extends="struts-default">
- <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >
- <result name="success">/WEB-INF/page/hello.jsp</result>
- </action>
- </package>
- </struts>
Struts.xml配置中的包介紹
- <package name="itcast" namespace="/test" extends="struts-default">
- <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >
- <result name="success">/WEB-INF/page/hello.jsp</result>
- </action>
- </package>
在struts2框架中使用包來管理Action,包的作用和java中的類包是非常類似的,它主要用於管理一組業務功能相關的action。在實際應用中,我們應該把一組業務功能相關的Action放在同一個包下。
name屬性
配置包時必須指定name屬性,該name屬性值可以任意取名,但必須唯一,他不對應java的類包,如果其他包要繼承該包,必須通過該屬性進行引用。
namespace屬性
包的namespace屬性用於定義該包的命名空間,命名空間作爲訪問該包下Action的路徑的一部分,如訪問上面例子的Action,訪問路徑爲:/test/helloworld.action。 namespace屬性可以不配置,對本例而言,如果不指定該屬性,默認的命名空間爲“”(空字符串)。
通常每個包都應該繼承struts-default包, 因爲Struts2很多核心的功能都是攔截器來實現。如:從請求中把請求參數封裝到action、文件上傳和數據驗證等等都是通過攔截器實現的。 struts-default定義了這些攔截器和Result類型。可以這麼說:當包繼承了struts-default才能使用struts2提供的核心功能。 struts-default包是在struts2-core-2.x.x.jar文件中的struts-default.xml中定義。
struts-default.xml也是Struts2默認配置文件。 Struts2每次都會自動加載 struts-default.xml文件。
包還可以通過abstract=“true”定義爲抽象包,抽象包中不能包含action。
例子中使用到的cn.itcast.action.HelloWorldAction類如下:
- package cn.itcast.action;
- public class HelloWorldAction{
- private String message;
- public String getMessage() {
- return message;
- }
- public void setMessage(String message) {
- this.message = message;
- }
- public String execute() {
- this.message = "我的第一個struts2應用";
- return "success";
- }
- }
例子中使用到的/WEB-INF/page/hello.jsp如下:
- <%@ page language="java" pageEncoding="UTF-8"%>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <title>第一個struts2應用</title>
- </head>
- <body>
- ${message } <br>
- </body>
- </html>
可以使用EL表達式訪問Action中的屬性。
訪問HelloWorld應用
在struts1中,通過<action path=“/test/helloworld”>節點的path屬性指定訪問該action的URL路徑。在struts2中,情況就不是這樣了,訪問struts2中action的URL路徑由兩部份組成:包的命名空間+action的名稱,例如訪問本例子HelloWorldAction的URL路徑爲:/test/helloworld
(注意:完整路徑爲:http://localhost:端口/內容路徑/test/helloworld)。另外我們也可以加上.action後綴訪問此Action。
- <package name="itcast" namespace="/test" extends="struts-default">
- <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >
- <result name="success">/WEB-INF/page/hello.jsp</result>
- </action>
- </package>
Action名稱的搜索順序
1.獲得請求路徑的URI,例如url是:http://server/struts2/path1/path2/path3/test.action
2.首先尋找namespace爲/path1/path2/path3的package,如果不存在這個package則執行步驟3;如果存在這個package,則在這個package中尋找名字爲test的action,當在該package下尋找不到action 時就會直接跑到默認namaspace的package裏面去尋找action(默認的命名空間爲空字符串“” ) ,如果在默認namaspace的package裏面還尋找不到該action,頁面提示找不到action
3.尋找namespace爲/path1/path2的package,如果不存在這個package,則轉至步驟4;如果存在這個package,則在這個package中尋找名字爲test的action,當在該package中尋找不到action 時就會直接跑到默認namaspace的package裏面去找名字爲test的action ,在默認namaspace的package裏面還尋找不到該action,頁面提示找不到action
4.尋找namespace爲/path1的package,如果不存在這個package則執行步驟5;如果存在這個package,則在這個package中尋找名字爲test的action,當在該package中尋找不到action 時就會直接跑到默認namaspace的package裏面去找名字爲test的action ,在默認namaspace的package裏面還尋找不到該action,頁面提示找不到action
5.尋找namespace爲/的package,如果存在這個package,則在這個package中尋找名字爲test的action,當在package中尋找不到action或者不存在這個package時,都會去默認namaspace的package裏面尋找action,如果還是找不到,頁面提示找不到action。
Action配置中的各項默認值
- <package name="itcast" namespace="/test" extends="struts-default">
- <action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute" >
- <result name="success">/WEB-INF/page/hello.jsp</result>
- </action>
- </package>
2>如果沒有爲action指定method,默認執行action中的execute() 方法。
3>如果沒有指定result的name屬性,默認值爲success。
Action中result的各種轉發類型
- <action name="helloworld" class="cn.itcast.action.HelloWorldAction">
- <result name="success">/WEB-INF/page/hello.jsp</result>
- </action>
在result中還可以使用${屬性名}表達式訪問action中的屬性,表達式裏的屬性名對應action中的屬性。如下:
<result type="redirect">/view.jsp?id=${id}</result>
下面是redirectAction 結果類型的例子,如果重定向的action中同一個包下:
- <result type="redirectAction">helloworld</result>
如果重定向的action在別的命名空間下:
- <result type="redirectAction">
- <param name="actionName">helloworld</param>
- <param name="namespace">/test</param>
- </result>
- <result name="source" type="plainText ">
- <param name="location">/xxx.jsp</param>
- <param name="charSet">UTF-8</param><!-- 指定讀取文件的編碼 -->
- </result>
多個Action共享一個視圖--全局result配置
當多個action中都使用到了相同視圖,這時我們應該把result定義爲全局視圖。struts1中提供了全局forward,struts2中也提供了相似功能:
- <package ....>
- <global-results>
- <result name="message">/message.jsp</result>
- </global-results>
- </package>
Action的屬性注入值
Struts2爲Action中的屬性提供了依賴注入功能,在struts2的配置文件中,我們可以很方便地爲Action中的屬性注入值。注意:屬性必須提供setter方法。
- public class HelloWorldAction{
- private String savePath;
- public String getSavePath() {
- return savePath;
- }
- public void setSavePath(String savePath) {
- this.savePath = savePath;
- }
- ......
- }
- <package name="itcast" namespace="/test" extends="struts-default">
- <action name="helloworld" class="cn.itcast.action.HelloWorldAction" >
- <param name="savePath">/images</param>
- <result name="success">/WEB-INF/page/hello.jsp</result>
- </action>
- </package>
上面通過<param>節點爲action的savePath屬性注入“/images”
指定需要Struts 2處理的請求後綴
前面我們都是默認使用.action後綴訪問Action。其實默認後綴是可以通過常量”struts.action.extension“進行修改的,例如:我們可以配置Struts 2只處理以.do爲後綴的請求路徑:
- <?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.action.extension" value="do"/>
- </struts>
如果用戶需要指定多個請求後綴,則多個後綴之間以英文逗號(,)隔開。如:
- <constant name="struts.action.extension" value="do,go"/>
細說常量定義
常量可以在struts.xml或struts.properties中配置,建議在struts.xml中配置,兩種配置方式如下:
在struts.xml文件中配置常量
- <struts>
- <constant name="struts.action.extension" value="do"/>
- </struts>
在struts.properties中配置常量
struts.action.extension=do
因爲常量可以在下面多個配置文件中進行定義,所以我們需要了解struts2加載常量的搜索順序:
struts-default.xml
struts-plugin.xml
struts.xml
struts.properties
web.xml
如果在多個文件中配置了同一個常量,則後一個文件中配置的常量值會覆蓋前面文件中配置的常量值.
常用的常量介紹
<!-- 指定默認編碼集,作用於HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的輸出 -->
<constant name="struts.i18n.encoding" value="UTF-8"/>
<!-- 該屬性指定需要Struts 2處理的請求後綴,該屬性的默認值是action,即所有匹配*.action的請求都由Struts2處理。
如果用戶需要指定多個請求後綴,則多個後綴之間以英文逗號(,)隔開。 -->
<constant name="struts.action.extension" value="do"/>
<!-- 設置瀏覽器是否緩存靜態內容,默認值爲true(生產環境下使用),開發階段最好關閉 -->
<constant name="struts.serve.static.browserCache" value="false"/>
<!-- 當struts的配置文件修改後,系統是否自動重新加載該文件,默認值爲false(生產環境下使用),開發階段最好打開 -->
<constant name="struts.configuration.xml.reload" value="true"/>
<!-- 開發模式下使用,這樣可以打印出更詳細的錯誤信息 -->
<constant name="struts.devMode" value="true" />
<!-- 默認的視圖主題 -->
<constant name="struts.ui.theme" value="simple" />
<!– 與spring集成時,指定由spring負責action對象的創建 -->
<constant name="struts.objectFactory" value="spring" />
<!–該屬性設置Struts 2是否支持動態方法調用,該屬性的默認值是true。如果需要關閉動態方法調用,則可設置該屬性爲false。 -->
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<!--上傳文件的大小限制-->
<constant name="struts.multipart.maxSize" value=“10701096"/>
Struts2的處理流程
StrutsPrepareAndExecuteFilter是Struts 2框架的核心控制器,它負責攔截由<url-pattern>/*</url-pattern>指定的所有用戶請求,當用戶請求到達時,該Filter會過濾用戶的請求。默認情況下,如果用戶請求的路徑不帶後綴或者後綴以.action結尾,這時請求將被轉入Struts 2框架處理,否則Struts 2框架將略過該請求的處理。當請求轉入Struts
2框架處理時會先經過一系列的攔截器,然後再到Action。與Struts1不同,Struts2對用戶的每一次請求都會創建一個Action,所以Struts2中的Action是線程安全的。
爲應用指定多個struts配置文件
在大部分應用裏,隨着應用規模的增加,系統中Action的數量也會大量增加,導致struts.xml配置文件變得非常臃腫。爲了避免struts.xml文件過於龐大、臃腫,提高struts.xml文件的可讀性,我們可以將一個struts.xml配置文件分解成多個配置文件,然後在struts.xml文件中包含其他配置文件。下面的struts.xml通過<include>元素指定多個配置文件:
- <?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>
- <include file="struts-user.xml"/>
- <include file="struts-order.xml"/>
- </struts>
通過這種方式,我們就可以將Struts 2的Action按模塊添加在多個配置文件中。
動態方法調用
如果Action中存在多個方法時,我們可以使用!+方法名調用指定方法。如下:
- public class HelloWorldAction{
- private String message;
- ....
- public String execute() throws Exception{
- this.message = "我的第一個struts2應用";
- return "success";
- }
- public String other() throws Exception{
- this.message = "第二個方法";
- return "success";
- }
- }
假設訪問上面action的URL路徑爲: /struts/test/helloworld.action
要訪問action的other() 方法,我們可以這樣調用:
/struts/test/helloworld!other.action
如果不想使用動態方法調用,我們可以通過常量struts.enable.DynamicMethodInvocation關閉動態方法調用。
- <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
使用通配符定義action
- <package name="itcast" namespace="/test" extends="struts-default">
- <action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">
- <result name="success">/WEB-INF/page/hello.jsp</result>
- </action>
- </package>
- public class HelloWorldAction{
- private String message;
- ....
- public String execute() throws Exception{
- this.message = "我的第一個struts2應用";
- return "success";
- }
- public String other() throws Exception{
- this.message = "第二個方法";
- return "success";
- }
- }
要訪問other()方法,可以通過這樣的URL訪問:/test/helloworld_other.action
接收請求參數
採用基本類型接收請求參數(get/post)
在Action類中定義與請求參數同名的屬性,struts2便能自動接收請求參數並賦予給同名屬性。請求路徑: http://localhost:8080/test/view.action?id=78
- public class ProductAction {
- private Integer id;
- public void setId(Integer id) {//struts2通過反射技術調用與請求參數同名的屬性的setter方法來獲取請求參數值
- this.id = id;
- }
- public Integer getId() {return id;}
- }
採用複合類型接收請求參數
請求路徑: http://localhost:8080/test/view.action?product.id=78- public class ProductAction {
- private Product product;
- public void setProduct(Product product) { this.product = product; }
- public Product getProduct() {return product;}
- }
Struts2首先通過反射技術調用Product的默認構造器創建product對象,然後再通過反射技術調用product中與請求參數同名的屬性的setter方法來獲取請求參數值。
關於struts2.1.6接收中文請求參數亂碼問題
struts2.1.6版本中存在一個Bug,即接收到的中文請求參數爲亂碼(以post方式提交),原因是struts2.1.6在獲取並使用了請求參數後才調用HttpServletRequest的setCharacterEncoding()方法進行編碼設置 ,導致應用使用的就是亂碼請求參數。這個bug在struts2.1.8中已經被解決,如果你使用的是struts2.1.6,要解決這個問題,你可以這樣做:新建一個Filter,把這個Filter放置在Struts2的Filter之前,然後在doFilter()方法裏添加以下代碼
- public void doFilter(...){
- HttpServletRequest req = (HttpServletRequest) request;
- req.setCharacterEncoding("UTF-8");//應根據你使用的編碼替換UTF-8
- filterchain.doFilter(request, response);
- }
自定義類型轉換器
java.util.Date類型的屬性可以接收格式爲2009-07-20的請求參數值。但如果我們需要接收格式爲20091221的請求參數,我們必須定義類型轉換器,否則struts2無法自動完成類型轉換。
- import java.util.Date;
- public class HelloWorldAction {
- private Date createtime;
- public Date getCreatetime() {
- return createtime;
- }
- public void setCreatetime(Date createtime) {
- this.createtime = createtime;
- }
- }
- public class DateConverter extends DefaultTypeConverter {
- @Override public Object convertValue(Map context, Object value, Class toType) {
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
- try {
- if(toType == Date.class){//當字符串向Date類型轉換時
- String[] params = (String[]) value;// Request.getParameterValues()
- return dateFormat.parse(params[0]);
- }else if(toType == String.class){//當Date轉換成字符串時
- Date date = (Date) value;
- return dateFormat.format(date);
- }
- } catch (ParseException e) {}
- return null;
- }
- }
將上面的類型轉換器註冊爲局部類型轉換器:
在Action類所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的類名,後面的-conversion.properties是固定寫法,對於本例而言,文件的名稱應爲HelloWorldAction-conversion.properties 。在properties文件中的內容爲:
屬性名稱=類型轉換器的全類名
對於本例而言, HelloWorldAction-conversion.properties文件中的內容爲:
createtime= cn.itcast.conversion.DateConverter
自定義全局類型轉換器
將上面的類型轉換器註冊爲全局類型轉換器:
在WEB-INF/classes下放置xwork-conversion.properties文件 。在properties文件中的內容爲:
待轉換的類型=類型轉換器的全類名
對於本例而言, xwork-conversion.properties文件中的內容爲:
java.util.Date= cn.itcast.conversion.DateConverter
訪問或添加request/session/application屬性
- public String scope() throws Exception{
- ActionContext ctx = ActionContext.getContext();
- ctx.getApplication().put("app", "應用範圍");//往ServletContext裏放入app
- ctx.getSession().put("ses", "session範圍");//往session裏放入ses
- ctx.put("req", "request範圍");//往request裏放入req
- return "scope";
- }
JSP:
- <body>
- ${applicationScope.app} <br>
- ${sessionScope.ses}<br>
- ${requestScope.req}<br>
- </body>
獲取HttpServletRequest / HttpSession / ServletContext / HttpServletResponse對象
方法一,通過ServletActionContext.類直接獲取:
- public String rsa() throws Exception{
- HttpServletRequest request = ServletActionContext.getRequest();
- ServletContext servletContext = ServletActionContext.getServletContext();
- request.getSession()
- HttpServletResponse response = ServletActionContext.getResponse();
- return "scope";
- }
方法二,實現指定接口,由struts框架運行時注入:
- public class HelloWorldAction implements ServletRequestAware, ServletResponseAware, ServletContextAware{
- private HttpServletRequest request;
- private ServletContext servletContext;
- private HttpServletResponse response;
- public void setServletRequest(HttpServletRequest req) {
- this.request=req;
- }
- public void setServletResponse(HttpServletResponse res) {
- this.response=res;
- }
- public void setServletContext(ServletContext ser) {
- this.servletContext=ser;
- }
- }
文件上傳
第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar。這兩個文件可以從http://commons.apache.org/下載。
第二步:把form表的enctype設置爲:“multipart/form-data“,如下:
- <form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post">
- <input type="file" name="uploadImage">
- </form>
- public class HelloWorldAction{
- private File uploadImage;//得到上傳的文件
- private String uploadImageContentType;//得到文件的類型
- private String uploadImageFileName;//得到文件的名稱
- //這裏略省了屬性的getter/setter方法
- public String upload() throws Exception{
- String realpath = ServletActionContext.getServletContext().getRealPath("/images");
- File file = new File(realpath);
- if(!file.exists()) file.mkdirs();
- FileUtils.copyFile(uploadImage, new File(file, uploadImageFileName));
- return "success";
- }
- }
多文件上傳
第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar。這兩個文件可以從http://commons.apache.org/下載。
第二步:把form表的enctype設置爲:“multipart/form-data“,如下:
- <form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post">
- <input type="file" name="uploadImages">
- <input type="file" name="uploadImages">
- </form>
- public class HelloWorldAction {
- private File[] uploadImages;// 得到上傳的文件
- private String[] uploadImagesContentType;// 得到文件的類型
- private String[] uploadImagesFileName;// 得到文件的名稱
- // 這裏略省了屬性的getter/setter方法
- public String upload() throws Exception {
- String realpath = ServletActionContext.getServletContext().getRealPath(
- "/images");
- File file = new File(realpath);
- if (!file.exists())
- file.mkdirs();
- for (int i = 0; i < uploadImages.length; i++) {
- File uploadImage = uploadImages[i];
- FileUtils.copyFile(uploadImage, new File(file,
- uploadImagesFileName[i]));
- }
- return "success";
- }
- }
自定義攔截器
要自定義攔截器需要實現com.opensymphony.xwork2.interceptor.Interceptor接口:
- public class PermissionInterceptor implements Interceptor {
- private static final long serialVersionUID = -5178310397732210602L;
- public void destroy() {
- }
- public void init() {
- }
- public String intercept(ActionInvocation invocation) throws Exception {
- System.out.println("進入攔截器");
- if(session裏存在用戶){
- String result = invocation.invoke();
- }else{
- return “logon”;
- }
- //System.out.println("返回值:"+ result);
- //return result;
- }
- }
- <package name="itcast" namespace="/test" extends="struts-default">
- <interceptors>
- <interceptor name=“permission" class="cn.itcast.aop.PermissionInterceptor" />
- <interceptor-stack name="permissionStack">
- <interceptor-ref name="defaultStack" />
- <interceptor-ref name=" permission " />
- </interceptor-stack>
- </interceptors>
- <action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">
- <result name="success">/WEB-INF/page/hello.jsp</result>
- <interceptor-ref name="permissionStack"/>
- </action>
- </package>
因爲struts2中如文件上傳,數據驗證,封裝請求參數到action等功能都是由系統默認的defaultStack中的攔截器實現的,所以我們定義的攔截器需要引用系統默認的defaultStack,這樣應用纔可以使用struts2框架提供的衆多功能。
如果希望包下的所有action都使用自定義的攔截器,可以通過<default-interceptor-ref name=“permissionStack”/>把攔截器定義爲默認攔截器。注意:每個包只能指定一個默認攔截器。另外,一旦我們爲該包中的某個action顯式指定了某個攔截器,則默認攔截器不會起作用。
輸入校驗
在struts2中,我們可以實現對action的所有方法進行校驗或者對action的指定方法進行校驗。
對於輸入校驗struts2提供了兩種實現方法:
1. 採用手工編寫代碼實現。
2. 基於XML配置方式實現。
手工編寫代碼實現對action中所有方法輸入校驗
通過重寫validate() 方法實現, validate()方法會校驗action中所有與execute方法簽名相同的方法。當某個數據校驗失敗時,我們應該調用addFieldError()方法往系統的fieldErrors添加校驗失敗信息(爲了使用addFieldError()方法,action可以繼承ActionSupport ),如果系統的fieldErrors包含失敗信息,struts2會將請求轉發到名爲input的result。在input視圖中可以通過<s:fielderror/>顯示失敗信息。
validate()使用例子:
- public void validate() {
- if(this.mobile==null || "".equals(this.mobile.trim())){ this.addFieldError("username", "手機號不能爲空");
- }else{ if(!Pattern.compile("^1[358]\\d{9}").matcher(this.mobile.trim()).matches()){
- this.addFieldError(“mobile", "手機號的格式不正確"); }
- }
- }
驗證失敗後,請求轉發至input視圖:
- <result name="input">/WEB-INF/page/addUser.jsp</result>
在addUser.jsp頁面中使用<s:fielderror/>顯示失敗信息。
手工編寫代碼實現對action指定方法輸入校驗
通過validateXxx()方法實現, validateXxx()只會校驗action中方法名爲Xxx的方法。其中Xxx的第一個字母要大寫。當某個數據校驗失敗時,我們應該調用addFieldError()方法往系統的fieldErrors添加校驗失敗信息(爲了使用addFieldError()方法,action可以繼承ActionSupport ),如果系統的fieldErrors包含失敗信息,struts2會將請求轉發到名爲input的result。在input視圖中可以通過<s:fielderror/>顯示失敗信息。
validateXxx()方法使用例子:
- public String add() throws Exception{ return "success";}
- public void validateAdd(){
- if(username==null && "".equals(username.trim())) this.addFieldError("username", "用戶名不能爲空");
- }
驗證失敗後,請求轉發至input視圖:
- <result name="input">/WEB-INF/page/addUser.jsp</result>
在addUser.jsp頁面中使用<s:fielderror/>顯示失敗信息。
輸入校驗的流程
1。類型轉換器對請求參數執行類型轉換,並把轉換後的值賦給action中的屬性。
2。如果在執行類型轉換的過程中出現異常,系統會將異常信息保存到ActionContext,conversionError攔截器將異常信息添加到fieldErrors裏。不管類型轉換是否出現異常,都會進入第3步。
3。系統通過反射技術先調用action中的validateXxx()方法,Xxx爲方法名。
4。再調用action中的validate()方法。
5。經過上面4步,如果系統中的fieldErrors存在錯誤信息(即存放錯誤信息的集合的size大於0),系統自動將請求轉發至名稱爲input的視圖。如果系統中的fieldErrors沒有任何錯誤信息,系統將執行action中的處理方法。
基於XML配置方式實現對action的所有方法進行輸入校驗
使用基於XML配置方式實現輸入校驗時,Action也需要繼承ActionSupport,並且提供校驗文件,校驗文件和action類放在同一個包下,文件的取名格式爲:ActionClassName-validation.xml,其中ActionClassName爲action的簡單類名,-validation爲固定寫法。如果Action類爲cn.itcast.UserAction,那麼該文件的取名應爲:UserAction-validation.xml。下面是校驗文件的模版:
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.3//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
- <validators>
- <field name="username">
- <field-validator type="requiredstring">
- <param name="trim">true</param>
- <message>用戶名不能爲空!</message>
- </field-validator>
- </field>
- </validators>
<field>指定action中要校驗的屬性,<field-validator>指定校驗器,上面指定的校驗器requiredstring是由系統提供的,系統提供了能滿足大部分驗證需求的校驗器,這些校驗器的定義可以在xwork-2.x.jar中的com.opensymphony.xwork2.validator.validators下的default.xml中找到。
<message>爲校驗失敗後的提示信息,如果需要國際化,可以爲message指定key屬性,key的值爲資源文件中的key。
在這個校驗文件中,對action中字符串類型的username屬性進行驗證,首先要求調用trim()方法去掉空格,然後判斷用戶名是否爲空。
編寫校驗文件時,不能出現幫助信息
在編寫ActionClassName-validation.xml校驗文件時,如果出現不了幫助信息,可以按下面方式解決:
windwos->preferences->myeclipse->files and editors->xml->xmlcatalog
點“add”,在出現的窗口中的location中選“File system”,然後在xwork-2.1.2解壓目錄的src\java目錄中選擇xwork-validator-1.0.3.dtd,回到設置窗口的時候不要急着關閉窗口,應把窗口中的Key Type改爲URI 。Key改爲http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd
struts2提供的校驗器列表
系統提供的校驗器如下:
required (必填校驗器,要求field的值不能爲null)
requiredstring (必填字符串校驗器,要求field的值不能爲null,並且長度大於0,默認情況下會對字符串去前後空格)
stringlength(字符串長度校驗器,要求field的值必須在指定的範圍內,否則校驗失敗,minLength參數指定最小長度,maxLength參數指定最大長度,trim參數指定校驗field之前是否去除字符串前後的空格)
regex(正則表達式校驗器,檢查被校驗的field是否匹配一個正則表達式.expression參數指定正則表達式,caseSensitive參數指定進行正則表達式匹配時,是否區分大小寫,默認值爲true)
int(整數校驗器,要求field的整數值必須在指定範圍內,min指定最小值,max指定最大值)
double(雙精度浮點數校驗器,要求field的雙精度浮點數必須在指定範圍內,min指定最小值,max指定最大值)
fieldexpression(字段OGNL表達式校驗器,要求field滿足一個ognl表達式,expression參數指定ognl表達式,該邏輯表達式基於ValueStack進行求值,返回true時校驗通過,否則不通過)
email(郵件地址校驗器,要求如果field的值非空,則必須是合法的郵件地址)
url(網址校驗器,要求如果field的值非空,則必須是合法的url地址)
date(日期校驗器,要求field的日期值必須在指定範圍內,min指定最小值,max指定最大值)
conversion(轉換校驗器,指定在類型轉換失敗時,提示的錯誤信息)
visitor(用於校驗action中的複合屬性,它指定一個校驗文件用於校驗複合屬性中的屬性)
expression(OGNL表達式校驗器,expression參數指定ognl表達式,該邏輯表達式基於ValueStack進行求值,返回true時校驗通過,否則不通過,該校驗器不可用在字段校驗器風格的配置中)
校驗器的使用例子
required 必填校驗器
- <field-validator type="required">
- <message>性別不能爲空!</message>
- </field-validator>
requiredstring 必填字符串校驗器
- <field-validator type="requiredstring">
- <param name="trim">true</param>
- <message>用戶名不能爲空!</message>
- </field-validator>
stringlength:字符串長度校驗器
- <field-validator type="stringlength">
- <param name="maxLength">10</param>
- <param name="minLength">2</param>
- <param name="trim">true</param>
- <message><![CDATA[產品名稱應在2-10個字符之間]]></message>
- </field-validator>
email:郵件地址校驗器
- <field-validator type="email">
- <message>電子郵件地址無效</message>
- </field-validator>
regex:正則表達式校驗器
- <field-validator type="regex">
- <param name="expression"><![CDATA[^1[358]\d{9}$]]></param>
- <message>手機號格式不正確!</message>
- </field-validator>
int:整數校驗器
- <field-validator type="int">
- <param name="min">1</param>
- <param name="max">150</param>
- <message>年齡必須在1-150之間</message>
- </field-validator>
字段OGNL表達式校驗器
- <field name="imagefile">
- <field-validator type="fieldexpression">
- <param name="expression"><![CDATA[imagefile.length() <= 0]]></param>
- <message>文件不能爲空</message>
- </field-validator>
- </field>
基於XML配置方式對指定action方法實現輸入校驗
當校驗文件的取名爲ActionClassName-validation.xml時,會對 action中的所有處理方法實施輸入驗證。如果你只需要對action中的某個action方法實施校驗,那麼,校驗文件的取名應爲:ActionClassName-ActionName-validation.xml,其中ActionName爲struts.xml中action的名稱。例如:在實際應用中,常有以下配置:
- <action name="user_*" class="cn.itcast.action.UserAction" method="{1}“ >
- <result name="success">/WEB-INF/page/message.jsp</result>
- <result name="input">/WEB-INF/page/addUser.jsp</result>
- </action>
UserAction中有以下兩個處理方法:
- public String add() throws Exception{
- ....
- }
- public String update() throws Exception{
- ....
- }
要對add()方法實施驗證,校驗文件的取名爲: UserAction-user_add-validation.xml
要對update()方法實施驗證,校驗文件的取名爲: UserAction-user_update-validation.xml
基於XML校驗的一些特點
當爲某個action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml兩種規則的校驗文件時,系統按下面順序尋找校驗文件:
1。AconClassName-validation.xml
2。ActionClassName-ActionName-validation.xml
系統尋找到第一個校驗文件時還會繼續搜索後面的校驗文件,當搜索到所有校驗文件時,會把校驗文件裏的所有校驗規則彙總,然後全部應用於action方法的校驗。如果兩個校驗文件中指定的校驗規則衝突,則只使用後面文件中的校驗規則。
當action繼承了另一個action,父類action的校驗文件會先被搜索到。
假設UserAction繼承BaseAction:
- <action name="user" class="cn.itcast.action.UserAction" method="{1}">
- </action>
訪問上面action,系統先搜索父類的校驗文件:BaseAction-validation.xml, BaseAction-user-validation.xml,接着搜索子類的校驗文件: UserAction-validation.xml, UserAction-user-validation.xml。應用於上面action的校驗規則爲這四個文件的總和。
國際化
準備資源文件,資源文件的命名格式如下:
baseName_language_country.properties
baseName_language.properties
baseName.properties
其中baseName是資源文件的基本名,我們可以自定義,但language和country必須是java支持的語言和國家。如:
中國大陸: baseName_zh_CN.properties
美國: baseName_en_US.properties
現在爲應用添加兩個資源文件:
第一個存放中文:itcast_zh_CN.properties
內容爲:welcome=歡迎來到傳智播客
第二個存放英語(美國): itcast_en_US.properties
內容爲: welcome=welcome to itcast
對於中文的屬性文件,我們編寫好後,應該使用jdk提供的native2ascii命令把文件轉換爲unicode編碼的文件。命令的使用方式如下:
native2ascii 源文件.properties 目標文件.properties
配置全局資源與輸出國際化信息
當準備好資源文件之後,我們可以在struts.xml中通過struts.custom.i18n.resources常量把資源文件定義爲全局資源文件,如下:
- <constant name="struts.custom.i18n.resources" value="itcast" />
後面我們就可以在頁面或在action中訪問國際化信息:
在JSP頁面中使用<s:text name=“”/>標籤輸出國際化信息:
<s:text name=“user”/>,name爲資源文件中的key
在Action類中,可以繼承ActionSupport,使用getText()方法得到國際化信息,該方法的第一個參數用於指定資源文件中的key。
在表單標籤中,通過key屬性指定資源文件中的key,如:
- <s:textfield name="realname" key="user"/>
國際化—輸出帶佔位符的國際化信息
資源文件中的內容如下:
welcome= {0},歡迎來到傳智播客{1}
在jsp頁面中輸出帶佔位符的國際化信息
- <s:text name="welcome">
- <s:param><s:property value="realname"/></s:param>
- <s:param>學習</s:param>
- </s:text>
在Action類中獲取帶佔位符的國際化信息,可以使用getText(String key, String[] args)或getText(String aTextName, List args)方法。
國際化—包範圍資源文件
在一個大型應用中,整個應用有大量的內容需要實現國際化,如果我們把國際化的內容都放置在全局資源屬性文件中,顯然會導致資源文件變的過於龐大、臃腫,不便於維護,這個時候我們可以針對不同模塊,使用包範圍來組織國際化文件。
方法如下:
在java的包下放置package_language_country.properties資源文件,package爲固定寫法,處於該包及子包下的action都可以訪問該資源。當查找指定key的消息時,系統會先從package資源文件查找,當找不到對應的key時,纔會從常量struts.custom.i18n.resources指定的資源文件中尋找。
國際化—Action範圍資源文件
我們也可以爲某個action單獨指定資源文件,方法如下:
在Action類所在的路徑,放置ActionClassName_language_country.properties資源文件,ActionClassName爲action類的簡單名稱。
當查找指定key的消息時,系統會先從ActionClassName_language_country.properties資源文件查找,如果沒有找到對應的key,然後沿着當前包往上查找基本名爲package 的資源文件,一直找到最頂層包。如果還沒有找到對應的key,最後會從常量struts.custom.i18n.resources指定的資源文件中尋找。
國際化—JSP中直接訪問某個資源文件
struts2爲我們提供了<s:i18n>標籤,使用<s:i18n>標籤我們可以在類路徑下直接從某個資源文件中獲取國際化數據,而無需任何配置:
- <s:i18n name="itcast">
- <s:text name=“welcome”/>
- </s:i18n>
如果要訪問的資源文件在類路徑的某個包下,可以這樣訪問:
- <s:i18n name=“cn/itcast/action/package">
- <s:text name="welcome">
- <s:param>小張</s:param>
- </s:text>
- </s:i18n>
OGNL表達式語言
OGNL是Object Graphic Navigation Language(對象圖導航語言)的縮寫,它是一個開源項目。 Struts 2框架使用OGNL作爲默認的表達式語言。
相對EL表達式,它提供了平時我們需要的一些功能,如:
支持對象方法調用,如xxx.sayHello();
支持類靜態方法調用和值訪問,表達式的格式爲@[類全名(包括包路徑)]@[方法名 | 值名],例如:@java.lang.String@format('foo %s', 'bar')或@cn.itcast.Constant@APP_NAME;
操作集合對象。
Ognl 有一個上下文(Context)概念,說白了上下文就是一個MAP結構,它實現了java.utils.Map接口,在Struts2中上下文(Context)的實現爲ActionContext,下面是上下文(Context)的結構示意圖
Struts 2中的OGNL Context實現者爲ActionContext,它結構示意圖如下:
當Struts2接受一個請求時,會迅速創建ActionContext,ValueStack,action 。然後把action存放進ValueStack,所以action的實例變量可以被OGNL訪問。
訪問上下文(Context)中的對象需要使用#符號標註命名空間,如#application、#session
另外OGNL會設定一個根對象(root對象),在Struts2中根對象就是ValueStack(值棧) 。如果要訪問根對象(即ValueStack)中對象的屬性,則可以省略#命名空間,直接訪問該對象的屬性即可。
在struts2中,根對象ValueStack的實現類爲OgnlValueStack,該對象不是我們想像的只存放單個值,而是存放一組對象。在OgnlValueStack類裏有一個List類型的root變量,就是使用他存放一組對象
|--request
|--application
context ------|--OgnlValueStack root變量[action, OgnlUtil, ... ]
|--session
|--attr
|--parameters
在root變量中處於第一位的對象叫棧頂對象。通常我們在OGNL表達式裏直接寫上屬性的名稱即可訪問root變量裏對象的屬性,搜索順序是從棧頂對象開始尋找,如果棧頂對象不存在該屬性,就會從第二個對象尋找,如果沒有找到就從第三個對象尋找,依次往下訪問,直到找到爲止。
大家注意: Struts2中,OGNL表達式需要配合Struts標籤纔可以使用。如:<s:property value="name"/>
由於ValueStack(值棧)是Struts 2中OGNL的根對象,如果用戶需要訪問值棧中的對象,在JSP頁面可以直接通過下面的EL表達式訪問ValueStack(值棧)中對象的屬性:
${foo} //獲得值棧中某個對象的foo屬性
如果訪問其他Context中的對象,由於他們不是根對象,所以在訪問時,需要添加#前綴。
application對象:用於訪問ServletContext,例如#application.userName或者#application['userName'],相當於調用ServletContext的getAttribute("username")。
session對象:用來訪問HttpSession,例如#session.userName或者#session['userName'],相當於調用session.getAttribute("userName")。
request對象:用來訪問HttpServletRequest屬性(attribute)的Map,例如#request.userName或者#request['userName'],相當於調用request.getAttribute("userName")。
parameters對象:用於訪問HTTP的請求參數,例如#parameters.userName或者#parameters['userName'],相當於調用request.getParameter("username")。
attr對象:用於按page->request->session->application順序訪問其屬性。
爲何使用EL表達式能夠訪問valueStack中對象的屬性
原因是Struts2對HttpServletRequest作了進一步的封裝。簡略代碼如下:
- public class StrutsRequestWrapper extends HttpServletRequestWrapper {
- public StrutsRequestWrapper(HttpServletRequest req) {
- super(req);
- }
- public Object getAttribute(String s) {
- ......
- ActionContext ctx = ActionContext.getContext();
- Object attribute = super.getAttribute(s);//先從request範圍獲取屬性值
- if (ctx != null) {
- if (attribute == null) {//如果從request範圍沒有找到屬性值,即從ValueStack中查找對象的屬性值
- ......
- ValueStack stack = ctx.getValueStack();
- attribute = stack.findValue(s);
- ......
- }
- }
- return attribute;
- }
- }
採用OGNL表達式創建List/Map集合對象
如果需要一個集合元素的時候(例如List對象或者Map對象),可以使用OGNL中同集合相關的表達式。
使用如下代碼直接生成一個List對象:
- <s:set name="list" value="{'zhangming','xiaoi','liming'}" />
- <s:iterator value="#list" id="n">
- <s:property value="n"/><br>
- </s:iterator>
生成一個Map對象:
- <s:set name="foobar" value="#{'foo1':'bar1', 'foo2':'bar2'}" />
- <s:iterator value="#foobar" >
- <s:property value="key"/>=<s:property value="value"/><br>
- </s:iterator>
Set標籤用於將某個值放入指定範圍。
scope:指定變量被放置的範圍,該屬性可以接受application、session、request、 page或action。如果沒有設置該屬性,則默認放置在OGNL Context中。
value:賦給變量的值.如果沒有設置該屬性,則將ValueStack棧頂的值賦給變量。
採用OGNL表達式判斷對象是否存在於集合中
對於集合類型,OGNL表達式可以使用in和not in兩個元素符號。其中,in表達式用來判斷某個元素是否在指定的集合對象中;not in判斷某個元素是否不在指定的集合對象中,如下所示。
in表達式:
- <s:if test="'foo' in {'foo','bar'}">
- 在
- </s:if>
- <s:else>
- 不在
- </s:else>
not in表達式:
- <s:if test="'foo' not in {'foo','bar'}">
- 不在
- </s:if>
- <s:else>
- 在
- </s:else>
OGNL表達式的投影功能
除了in和not in之外,OGNL還允許使用某個規則獲得集合對象的子集,常用的有以下3個相關操作符。
?:獲得所有符合邏輯的元素。
^:獲得符合邏輯的第一個元素。
$:獲得符合邏輯的最後一個元素。
例如代碼:
- <s:iterator value="books.{?#this.price > 35}">
- <s:property value="title" /> - $<s:property value="price" /><br>
- </s:iterator>
在上面代碼中,直接在集合後緊跟.{}運算符表明用於取出該集合的子集,{}內的表達式用於獲取符合條件的元素,this指的是爲了從大集合books篩選數據到小集合,需要對大集合books進行迭代,this代表當前迭代的元素。本例的表達式用於獲取集合中價格大於35的書集合。
- public class BookAction extends ActionSupport {
- private List<Book> books;
- ....
- @Override
- public String execute() {
- books = new LinkedList<Book>();
- books.add(new Book("A735619678", "spring", 67));
- books.add(new Book("B435555322", "ejb3.0",15));
- }
- }
property標籤
property標籤用於輸出指定值:
- <s:set name="name" value="'kk'" />
- <s:property value="#name"/>
default:可選屬性,如果需要輸出的屬性值爲null,則顯示該屬性指定的值
escape:可選屬性,指定是否格式化HTML代碼。
value:可選屬性,指定需要輸出的屬性值,如果沒有指定該屬性,則默認輸出ValueStack棧頂的值。
id:可選屬性,指定該元素的標識
iterator標籤
iterator標籤用於對集合進行迭代,這裏的集合包含List、Set和數組。
- <s:set name="list" value="{'zhangming','xiaoi','liming'}" />
- <s:iterator value="#list" status="st">
- <font color=<s:if test="#st.odd">red</s:if><s:else>blue</s:else>>
- <s:property /></font><br>
- </s:iterator>
value:可選屬性,指定被迭代的集合,如果沒有設置該屬性,則使用ValueStack棧頂的集合。
id:可選屬性,指定集合裏元素的id。
status:可選屬性,該屬性指定迭代時的IteratorStatus實例。該實例包含如下幾個方法:
int getCount(),返回當前迭代了幾個元素。
int getIndex(),返回當前迭代元素的索引。
boolean isEven(),返回當前被迭代元素的索引是否是偶數
boolean isOdd(),返回當前被迭代元素的索引是否是奇數
boolean isFirst(),返回當前被迭代元素是否是第一個元素。
boolean isLast(),返回當前被迭代元素是否是最後一個元素。
if/elseif/else標籤
- <s:set name="age" value="21" />
- <s:if test="#age==23">
- 23
- </s:if>
- <s:elseif test="#age==21">
- 21
- </s:elseif>
- <s:else>
- 都不等
- </s:else>
url標籤
- <s:url action="helloworld_add" namespace="/test"><s:param name="personid" value="23"/></s:url>
/struts/test/helloworld_add.action?personid=23
紅色部分爲內容路徑。
當標籤的屬性值作爲字符串類型處理時, “%”符號的用途是計算OGNL表達式的值。
- <s:set name="myurl" value="'http://www.foshanshop.net'"/>
- <s:url value="#myurl" /><br>
- <s:url value="%{#myurl}" />
輸出結果:
#myurl
http://www.foshanshop.net
表單標籤_checkboxlist複選框
如果集合爲list
- <s:checkboxlist name="list" list="{'Java','.Net','RoR','PHP'}" value="{'Java','.Net'}"/>
生成如下html代碼:
- <input type="checkbox" name="list" value="Java" checked="checked"/><label>Java</label>
- <input type="checkbox" name="list" value=".Net" checked="checked"/><label>.Net</label>
- <input type="checkbox" name="list" value="RoR"/><label>RoR</label>
- <input type="checkbox" name="list" value="PHP"/><label>PHP</label>
如果集合爲MAP
- <s:checkboxlist name="map" list="#{1:'瑜珈用品',2:'戶外用品',3:'球類',4:'自行車'}" listKey="key" listValue="value" value="{1,2,3}"/>
生成如下html代碼:
- <input type="checkbox" name="map" value="1" checked="checked"/><label>瑜珈用品</label>
- <input type="checkbox" name="map" value="2" checked="checked"/><label>戶外用品</label>
- <input type="checkbox" name="map" value="3" checked="checked"/><label>球類</label>
- <input type="checkbox" name="map" value="4"/><label>自行車</label>
如果集合裏存放的是javabean
- <%
- Person person1 = new Person(1,"第一個");
- Person person2 = new Person(2,"第二個");
- List<Person> list = new ArrayList<Person>();
- list.add(person1);
- list.add(person2);
- request.setAttribute("persons",list);
- %>
- <s:checkboxlist name="beans" list="#request.persons" listKey="personid" listValue="name"/>
Personid和name爲Person的屬性
生成如下html代碼:
- <input type="checkbox" name=“beans" value="1"/><label>第一個</label>
- <input type="checkbox" name=“beans" value="2"/><label>第二個</label>
表單標籤_radio單選框
該標籤的使用和checkboxlist複選框相同。
如果集合裏存放的是javabean(personid和name爲Person的屬性)
- < s:radio name="beans" list="#request.persons" listKey="personid" listValue="name"/>
生成如下html代碼:
- <input type="radio" name="beans" id="beans1" value="1"/><label>第一個</label>
- <input type="radio" name="beans" id="beans2" value="2"/><label>第二個</label>
如果集合爲MAP
- <s:radio name="map" list="#{1:'瑜珈用品',2:'戶外用品',3:'球類',4:'自行車'}" listKey="key" listValue="value“ value="1"/>
生成如下html代碼:
- <input type="radio" name="map" id="map1" value="1"/><label for="map1">瑜珈用品</label>
- <input type="radio" name="map" id="map2" value="2"/><label for="map2">戶外用品</label>
- <input type="radio" name="map" id="map3" value="3"/><label for="map3">球類</label>
- <input type="radio" name="map" id="map4" value="4"/><label for="map4">自行車</label>
如果集合爲list
- <s:radio name="list" list="{'Java','.Net'}" value="'Java'"/>
生成如下html代碼:
- <input type="radio" name="list" checked="checked" value="Java"/><label>Java</label>
- <input type="radio" name="list" value=".Net"/><label>.Net</label>
表單標籤_select下拉選擇框
- <s:select name="list" list="{'Java','.Net'}" value="'Java'"/>
- <select name="list" id="list">
- <option value="Java" selected="selected">Java</option>
- <option value=".Net">.Net</option>
- </select>
- <s:select name="beans" list="#request.persons" listKey="personid" listValue="name"/>
- <select name="beans" id="beans">
- <option value="1">第一個</option>
- <option value="2">第二個</option>
- </select>
- <s:select name="map" list="#{1:'瑜珈用品',2:'戶外用品',3:'球類',4:'自行車'}" listKey="key" listValue="value" value="1"/>
- <select name="map" id="map">
- <option value="1" selected="selected">瑜珈用品</option>
- <option value="2">戶外用品</option>
- <option value="3">球類</option>
- <option value="4">自行車</option>
- </select>
<s:token />標籤防止重複提交
<s:token />標籤防止重複提交,用法如下:
第一步:在表單中加入<s:token />
- <s:form action="helloworld_other" method="post" namespace="/test">
- <s:textfield name="person.name"/><s:token/><s:submit/>
- </s:form>
第二步:
- <action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">
- <interceptor-ref name="defaultStack" />
- <interceptor-ref name="token" />
- <result name="invalid.token">/WEB-INF/page/message.jsp</result>
- <result>/WEB-INF/page/result.jsp</result>
- </action>
以上配置加入了“token”攔截器和“invalid.token”結果,因爲“token”攔截器在會話的token與請求的token不一致時,將會直接返回“invalid.token”結果。
在debug狀態,控制檯出現下面信息,是因爲Action中並沒有struts.token和struts.token.name屬性,我們不用關心這個錯誤:
嚴重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting 'struts.token' on 'class xxx: Error setting expression 'struts.token' with value '[Ljava.lang.String;@39f16f'
嚴重: ParametersInterceptor - [setParameters]: Unexpected Exception caught setting 'struts.token.name'