天天看点

Structs2:拦截器以及表单重复提交的解决方案

一、拦截器

1、拦截器:拦截器和过滤器很相似。在 action 执行的前后执行。Struts2 的核心功能都是通过拦截器来实现。

2、拦截器栈:由多个拦截器组成。

3、作用:对于 action 的一些公共处理代码可以放到拦截器中来实现。如:权限控制,日志等等。

4、多个拦截器之间的执行是采用责任链设计模式来实现。

5、拦截器的执行流程

Structs2:拦截器以及表单重复提交的解决方案

6.自定义拦截器的实现步骤:

1)编写拦截器(实现Interceptor接口或者继承AbstractInterceptor类)

2)在structs.xml中配置拦截器

3)在action中引用拦截器

模拟structs2默认拦截器---timer,自定义拦截器实现

/*** *计算 action 执行时间 */ 
public class TimeInterceptor extends AbstractInterceptor{
    @Override 
    public String intercept(ActionInvocation invocation) throws Exception { 
        long start=System.currentTimeMillis(); 
        //执行下一个拦截器,当拦截器执行完后执行
        action String result= invocation.invoke();
        long end=System.currentTimeMillis();
        System.out.println("执行该 Action 所用时间为: "+(end-start)+"ms"); 
        return result;
     }
}
           

在structs.xml中配置

<struts>

<package name="default" extends="struts-default"  namespace="/">

<!-- 配置拦截器 -->

  <interceptors>

    <interceptor name="time"  class="cn.sxt.interceptor.TimeInterceptor"/>

  </interceptors>

  <action name="hello" class="cn.sxt.action.HelloAction">

    <result>/index.jsp</result>

    <!-- 应用拦截器 -->

     <interceptor-ref name="time"/>

   </action>

</package>

</struts>

当请求 hello.action 时将会执行该拦截器。

8、拦截器的配置详解

1)当引用了自定义拦截器后,默认的拦截器将不会生效,默认拦截器:在 struts-default.xml 中,当配置默认拦截器以后, 如果不引用拦截器,那么默认的拦截器将起作用。

在structs-default.xml中的声明

<default-interceptor-ref name="defaultStack"/>

2)当引用自定义拦截器后,又想使用 struts2 提供的拦截器功能,那么需要手动引用

<action name="hello" class="cn.sxt.action.HelloAction">

<result>/index.jsp</result>

<!-- 引用拦截器 -->

<interceptor-ref name="time"/>

<!-- 引用默认的拦截器栈 ;引用拦截器和引用拦截器栈的方式一样-->

<interceptor-ref name="defaultStack"/>

</action>

3)当 action 引用的拦截器个数比较多时,可以自定义拦截器栈,将多个拦截器放入一个拦截器栈中。

<!-- 一个拦截器栈中,可以包含多个拦截器的引用拦截器栈的引用和拦截器一致 -->

<interceptor-stack name="myStack">

<!-- 引用拦截器 -->

<interceptor-ref name="time"/>

<!-- 引用默认的拦截器栈 ;引用拦截器和引用拦截器栈的方式一样-->

<interceptor-ref name="defaultStack"/>

</interceptor-stack>

<action name="hello" class="cn.sxt.action.HelloAction">

<result>/index.jsp</result>

<interceptor-ref name="myStack"/>

</action>

4)当自定义拦截器栈在这个包下的所有 action 都使用的时,可以定义为默认的拦截器栈,或默认的拦截器。

<!-- 定义默认的拦截器/栈 -->

<default-interceptor-ref name="myStack"/>

<action name="hello" class="cn.sxt.action.HelloAction">

<result>/index.jsp</result>

</action>

案例:对于登录权限的控制

登录验证拦截器

public class LoginInterceptor extends AbstractInterceptor {
    @Override
    public String intercept(ActionInvocation actionInvocation) throws Exception {
        //判断是否是login.action,如果是logina.action则直接执行下一个执行器
        //如果不是,则通过session判断是否登陆,如果已经登陆,则执行下一个执行器
        //如果没有登陆则返回登陆页面
        String actionName = actionInvocation.getProxy().getActionName();
        if("login".equals(actionName)){
            return actionInvocation.invoke();
        }
        Object obj = actionInvocation.getInvocationContext().getSession().get("user");
        if(obj == null){
            return Action.LOGIN;
        }
        return actionInvocation.invoke();
    }
}
           

Struts.xml 的配置

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
        "http://struts.apache.org/dtds/struts-2.5.dtd">

<struts>
    <package name="hello" extends="struts-default" >
<!--        配置拦截器-->
        <interceptors>
            <interceptor name="LoginInterceptor" class="com.xijian.interceptor.LoginInterceptor"/>
<!--            一个拦截器栈中,可以包含多个拦截器的引用,在action中引入时和引入普通拦截器一扬-->
            <interceptor-stack name="myStack">
<!--                引用拦截器-->
                <interceptor-ref name="LoginInterceptor"></interceptor-ref>
                <interceptor-ref name="timer"></interceptor-ref>
<!--                引用默认的拦截器栈-->
                <interceptor-ref name="defaultStack"></interceptor-ref>

            </interceptor-stack>

        </interceptors>
<!--        定义默认的拦截器/栈,适用于所有action-->
        <default-interceptor-ref name="myStack"/>
<!--        定义全局结果集-->
        <global-results>
            <result name="login">/login.jsp</result>
        </global-results>
        <action name="hello" class="com.xijian.controller.HelloAction" method="hello">
            <result name="success">/index.jsp</result>
        </action>
        <action name="login" class="com.xijian.controller.LoginAction" method="login">
            <result>/success.jsp</result>
        </action>
    </package>

</struts>
           

login.jsp页面

<%--
  Created by IntelliJ IDEA.
  User: 席剑
  Date: 2019/10/15
  Time: 11:51
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="login.action" method="post">
        <input type="text" name="username">
        <input type="text" name="password">
        <input type="submit" value="login">
    </form>
</body>
</html>
           

LoginAction

/**
 * @author XiJian
 * @date 2019/10/15 - 11:48
 */
public class LoginAction implements ModelDriven<User> {
    public User user = new User();
    public User getModel(){
        return this.user;
    }
    public String login(){
        System.out.println(user.getUsername()+"-----"+user.getPassword());
        if("zhangsan".equals(user.getUsername()) && "123".equals(user.getPassword())) {
            ActionContext.getContext().getSession().put("user",user.getUsername());
            return Action.SUCCESS;
        }
        return Action.LOGIN;
    }
}
           

9.方法拦截器:方法拦截器时比 Action 拦截器更加细粒度的控制,主体实现和 Action 拦截器一致。但是方法拦截器时继承 MethodFilterInterceptor 类,重写 doIntercept()方法。引用方法拦截器配置会发生改变:

<interceptor-ref name="methodInterceptor"> 
<!-- 配置被拦截的方法 -->
 <param name="includeMethods">list,add</param>
 <!-- 配置不被拦截的方法 -->
 <param name="excludeMethods">login</param> 
</interceptor-ref>
           

二、表单重复提交问题的解决

即当前页面有form表单元素,用户在点击提交后重复刷新页面,可导致后台对应的Action接到多条重复数据,这显然是非常不合理的,在Structs2中框架提供给我们一种解决方案,就是引入 token 拦截器。

在jsp页面中也需要引入Structs2标签库,在form表单中引入一个<s:token>标签

当表单重复提交时,会返回invalid.token,可以根据这个返回结果跳转到对应错误页面。

原理是:在第一次提交表单时,会在session中存入一个token值(令牌),这个值也会随着响应写入到jsp页面中,当再次提交表单时,请求中会有此token码,然后发起请求时去session中验证,如果session中存在,即允许表单提交,如果session中此码不存在,证明是无效的提交。

当每一次提交成功后都会将session中的token码删除。

Structs2:拦截器以及表单重复提交的解决方案