2015年9月29日 星期二

攔截器 (Struts2.3.x 十)

※系統內鍵的攔截器

HelloInterceptor.java
public class HelloInterceptor extends ActionSupport{
    public void Hello(){
        System.out.println("Hello Interceptor!");
    }
}
和一般action一樣
struts.xml
<action name="hello" class="action.HelloInterceptor">
    <interceptor-ref name="timer" />
</action>
因為package有extends="struts-default",這個檔案在struts2-core-2.x.x.jar裡, 所以name可以是timer,還有很多,可以參考這裡
測試時,在瀏覽器的網址列最後打上hello!Hello.action, 記得struts.xml要加上<constant name="struts.enable.DynamicMethodInvocation" value="true" />

※自訂攔截器

TestInterceptor.java
public class TestInterceptor extends AbstractInterceptor {
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        System.out.println("攔截成功!");
        return invocation.invoke();
    }
}
繼承一個叫AbstractInterceptor的抽象類別,並覆寫方法,invocation.invoke()表示繼續執行

struts.xml
<interceptors>
    <interceptor name="testICR" class="interceptor.TestInterceptor" />
</interceptors>
<action name="hello" class="action.HelloInterceptor">
    <interceptor-ref name="timer" />
    <interceptor-ref name="testICR" />
</action>
可以有很多攔截器,這個例子有兩個,要注意interceptors要寫在action上面,錯誤會有提示

※多個攔截器設定

也就是上面的struts.xml可以改成以下的寫法

<interceptors>
    <!-- interceptor可以很多 -->
    <interceptor name="testICR" class="interceptor.TestInterceptor" />
    
    <interceptor-stack name="xxx">
        <interceptor-ref name="timer" />
        <interceptor-ref name="testICR" />
    </interceptor-stack>
</interceptors>

<action name="hello" class="action.HelloInterceptor">
    <interceptor-ref name="xxx" />
</action>
把interceptor-ref搬上去後,用interceptor-stack包起來,然後取個名字,下面就使用新的名字即可

※全域攔截器設定

<interceptors>
    <interceptor name="testICR" class="interceptor.TestInterceptor" />
    
    <interceptor-stack name="xxx">
        <interceptor-ref name="timer" />
        <interceptor-ref name="testICR" />
    </interceptor-stack>
    <default-interceptor-ref name="xxx" />
</interceptors>

<action name="hello1" class="action.HelloInterceptor1" />
<action name="hello2" class="action.HelloInterceptor2" />
<action name="hello3" class="action.HelloInterceptor3" />

default-interceptor-ref設定完,所有的actin在執行之前都會攔截

※defaultStack

攔截器有個defaultStack,主要是執行struts2的方法
<interceptor-ref name="defaultStack" />
不加時,會執行setContainer()-->execute()
加上後,會執行setContainer()-->validate()-->hasErrors()-->execute()
所以如果想用validate方法,就要設這一行

※模擬一個登入攔截

登入時,如果帳號或密碼沒輸入,就再原網頁不動

login.jsp
<form action="login.action" method="post">
    <input type="text" name="user.userName" /><br /> 
    <input type="password" name="user.userPassword" /><br /> 
    <input type="reset" />
    <input type="submit" />
</form>

struts.xml
<interceptors>
    <interceptor name="login" class="interceptor.LoginInterceptor" />
    <interceptor-stack name="stack">
        <interceptor-ref name="login" />
    </interceptor-stack>
</interceptors>

<action name="login" class="login.LoginAction" method="execute">
    <interceptor-ref name="stack" />
    <result name="login">/struts2/login.jsp</result>
    
    <!-- 方法一 -->
    <result name="success">/struts2/success.jsp</result>
    <!-- 方法二,只是測試redirectAction -->
    <result name="success" type="redirectAction">xxx.action</result>
</action>
<!-- 此action方法二才需要 -->
<action name="xxx" class="login.LoginAction" method="success">
    <result name="success">/struts2/success.jsp</result>
</action>

LoginInterceptor.java的intercept
HttpServletRequest req = ServletActionContext.getRequest();
User user = new User();
user.setUserName(req.getParameter("user.userName"));
user.setUserPassword(req.getParameter("user.userPassword"));

// 方法一
Map<String, Object> map = invocation.getInvocationContext()
        .getSession();
// 方法二
// Map<String, Object> map = ActionContext.getContext().getSession();
if (user.getUserName().isEmpty() || user.getUserPassword().isEmpty()) {
    //方法一
    HttpServletResponse res = ServletActionContext.getResponse();
    res.sendRedirect("/Struts2Demo/struts2/login.jsp");
    return null;
    //方法二
    // return ActionSupport.LOGIN;
} else {
    map.put("user", user);
    return invocation.invoke();
}

LoginAction.java,記得繼承ActionSupport
private User user;
//setter/getter
@Override
public String execute() throws Exception {
    return SUCCESS;
}
//此方法為struts.xml的方法二才需要
public String success() {
    return SUCCESS;
}

User.java
private String userName;
private String userPassword;
//setter/getter

success.jsp
<%@ taglib prefix="s" uri="/struts-tags"%>
<%@ page isELIgnored="false"%>

OGNL:
<ul>
    <li>帳號:<s:property value="#session.user.userName"/>
    <li>密碼:<s:property value="#session.user.userPassword"/>
</ul>

EL:
<ul>
    <li>帳號:${user.userName}
    <li>密碼:${user.userPassword}
</ul>

※用攔截器防止頁面重覆提交

以登入的例子修改struts.xml
<constant name="struts.custom.i18n.resources" value="Message" />
<interceptors>
    <interceptor name="login" class="interceptor.LoginInterceptor" />
    
    <interceptor-stack name="stack">
        <interceptor-ref name="login" />
        <interceptor-ref name="basicStack" />
        <interceptor-ref name="token" />
    </interceptor-stack>
</interceptors>
<action name="login" class="login.LoginAction" method="execute">
    <interceptor-ref name="stack" />
    <result name="login">/struts2/login.jsp</result>
    <result name="success">/struts2/success.jsp</result>
    <result name="invalid.token">/struts2/login.jsp</result>
</action>
注意invalid.token要設定

login.jsp增加兩行
<form action="login.action" method="post">
    <s:token name="ooo" />
    <s:actionerror />
    <input type="text" name="user.userName" /><br /> 
    <input type="password" name="user.userPassword" /><br /> 
    <input type="reset" />
    <input type="submit" />
</form>
s:token的name可以隨便打,網頁原始碼可看出會變成兩行hidden
s:actionerror為針測到重覆提交時所產生的訊息

Message.properties 訊息定義在這個檔裡,struts.xml要設定這個檔名
struts.messages.invalid.token=\u8868\u55AE\u91CD\u8986\u63D0\u4EA4\uFF01

可參考這裡
如果把jsp的s:token放在form外面,不用重覆提交,一次就出現Message.properties的訊息了,而且在控制台會出「Could not find token name in params.」的錯

沒有留言:

張貼留言