2010年8月15日 星期日

[Struts2]-實作Interceptor

首先先來了解struts2 framework的運作機制
要部署struts2 framework我們必須在web.xml中加入這段
<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>
當 FilterDispatcher 根據 action name 所對應到 struts.xml 中的 Action class 之後,
Struts2 核心會先呼叫 Interceptor Stack 中的所有 interceptors。
接著再呼叫 Action class 中的 execute() method,完成後再呼叫 Result 幫助我們排版,
這過程中都會使用到 OGNL 到 ValueStack 中幫助我們取得必要的資料。

Interceptor Stack
這裡我們先將重點擺在Interceptor。Interceptor Stack 是在整個流程中第一個被呼叫的,
也是最後一個被呼叫的。Interceptor Stack 中包含一集合的 interceptors,
而 interceptors 的排列方式是以 stack 的方式排列。

下面我們先寫一個簡單無牽扯自訂interceptor的範例。
首先我們先新增一個Login.jsp頁面。
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>login</title>
</head>
<body>
<s:form action="/example/Hello" method="post">
<s:textfield name="user_name" label="User Name:"/>
<s:submit/>
</s:form>
</body>
</html>
這個頁面會呼叫hello action,所以我們接下來編寫一個HelloWorld.java來處理這個呼叫。
package example;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.StrutsStatics;

public class HelloWorld extends ActionSupport {
  private String user_name;
  private String message;

  @Override
  public String execute() throws Exception {
    message = "Hello:" + user_name;
    return SUCCESS;
  }

  public String getUser_name() {
    return user_name;
  }

  public void setUser_name(String user_name) {
    this.user_name = user_name;
  }

  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }
}
hello action被呼叫時會自動執行excute這個method,當處理完後會藉由return "String"告訴
framework要將結果導向那個result頁面。

接下來我們再編寫一個HellowWorld.jsp來作為輸出頁面。
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>

<html>
<head>
<title>Hello World</title>
</head>
<body>
<h2><s:property value="message"/></h2>
</body>
</html>

至於如何將Login.jsp與HelloWorld.java做連結以及利用result標韱來控制輸出頁面呢?
則是在struts.xml中設定。
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="Hello" class="example.HelloWorld">
<result name="success">/example/HelloWorld.jsp</result>
<result name="login">/example/LOGIN.jsp</result>
</action>
</package>
</struts>
上述例子表示,
當action return "success"時會導向HelloWorld.jsp,
當return "login"時會導向Login.jsp。
這裡有兩個需要注意的地方:
1.在jsp頁面中呼叫action時,必須加入其package的namespace為prefix,
    如action="/example/Hello";
2.result的name屬性值是有區分大小寫的。


下面為這個範例的執行結果:
輸入名稱後
會出現歡迎頁面

接下來進入這篇的重點,我們要在這個流程中加入自訂的interceptor,
編寫一個AuthorizationInterceptor.java,
自訂的interceptor須繼承AbstractInterceptor介面。
package example;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class AuthorizationInterceptor extends AbstractInterceptor {
  @Override
  public String intercept(ActionInvocation ai) throws Exception {
    return ai.invoke();
  }
}
當interceptor發生作用時會呼叫intercept這個method。
而利用ActionInvocation的getInvocationContext()method,
我們可以得到儲存全文流程資料的ActionContext物件,這時就可以自由發揮了。

回頭看第一張framework流程圖,interceptor可以invoke原本request呼叫的action,
也可以直接return result頁面,
在這個例子中我們在interceptor中不做任何事,直接invoke action。

要讓自訂的interceptor發揮作用還需要在struts.xml中加以指明。
<struts>
<package name="example" namespace="/example" extends="struts-default">
<interceptors>
<interceptor name ="auth" class ="example.AuthorizationInterceptor"/>
</interceptors>
<action name="Hello" class="example.HelloWorld">
<interceptor-ref name ="auth"/>
<result name="success">/example/HelloWorld.jsp</result>
<result name="login">/example/LOGIN.jsp</result>
</action>
</package>
</struts>
表示呼叫hello action時必須先經過auth這個interceptor。

我們再一次的執行這個範例,確發現了意外的結果。
Login.jsp所輸入的使用者名稱無法帶到所呼叫的hello action,導致HelloWorld.jsp
頁面輸出時user_name欄位值為null。
這個原因是因為當我們自訂的interceptor呼叫invoke method時,整個flow會回到
framework的開頭階段,此時hello action並非由Login.jsp直接呼叫,
導致Login.jsp中的textfield欄位自動調用HellowWorld.java中的setUser_name()的機制失效。

此時hello action(HelloWorld.java)就必須自行由request中取出Login.jsp傳來的parameter。
將HellowWorld.java的execute method內容改寫如下:
public String execute() throws Exception {
ActionContext actionContext = ActionContext.getContext();
HttpServletRequest request= 
(HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
message = "Hello:" + request.getParameter("user_name");
return SUCCESS;
}
接著再一次執行修改過後的範例,就又可以看到正確的輸出結果了。

1 則留言:

  1. static-params interceptor 可以通過來call Action class 的相應 setter method

    < interceptor-ref name="params"/>

    回覆刪除