• 【转】Struts2解决表单重复提交问题


    用户重复提交表单在某些场合将会造成非常严重的后果。例如,在使用信用卡进行在线支付的时候,如果服务器的响应速度太慢,用户有可能会多次点击提交按钮,而这可能导致那张信用卡上的金额被消费了多次。因此,重复提交表单会对你的系统带来逻辑影响,必须采取一些措施防止这类情况的发生。

      用户重复提交同一个HTML表单的原因有: 一、快速多次点击了提交按钮;二、提交表单后按下浏览器的刷新按钮。


    设置Struts 2的预防表单重复提交的功能 

          Struts 2已经内置了能够防止用户重复提交同一个HTML表单的功能。它的工作原理:让服务器生成一个唯一标记,并在服务器和表单里各保存一份这个标记的副本。此后,在用户提交表单的时候,表单里的标记将随着其他请求参数一起发送到服务器,服务器将对他收到的标记和它留存的标记进行比较。如果两者匹配,这次提交的表单被认为是有效的,服务器将对之做出必要的处理并重新设置一个新标记。随后,提交相同的表单就会失败,因为服务器上的标记已经重置。 

      Struts 2标签中的token标签,可以用来生成一个独一无二的标记。这个标记必须嵌套在form标签中使用,它会在表单里插入一个隐藏字段并把标记保存到HttpSession对象里。toke标签必须与Token或Token Session拦截器配合使用,两个拦截器都能对token标签进行处理。Token拦截器遇到重复提交表单的情况,会返回一个"invalid.token"结果并加上一个动作级别的错误。Token Session拦截器扩展了Token拦截器并提供了一种更复杂的服务,它采取的做法与Token拦截器不同,它只是阻断了后续的提交,这样用户不提交多少次,就好像只是提交了一次。 


    示例:使用Token拦截器预防表单重复提交

    1.  配置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="avoidPackage" extends="struts-default">
            <action name="avoid" class="struts2.action.AvoidAction">
                <interceptor-ref name="token"></interceptor-ref>
                <interceptor-ref name="defaultStack"></interceptor-ref>     
                <result name="invalid.token">/error.jsp</result>
                <result name="success">/output.jsp</result>
            </action>
        </package>
    </struts>

    此时,需要在动作的声明中,为动作添加token拦截器,因为token拦截器不在defaultStack拦截器栈中,注意,需要将拦截器放在拦截器栈的第一位,这是因为判断表单是否被重复提交的逻辑应该在表单处理前。

    2. 创建动作类

    public class AvoidAction extends ActionSupport {
        private static final long serialVersionUID = 2676453800249807631L;   
        private String username;
        private Date birthday;
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public Date getBirthday() {
            return birthday;
        }
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }    
        @Override
        public String execute()
        {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }       
            return SUCCESS;
        }
    }

    这个动作逻辑处理为挂起4秒钟,让我们有机会多次点击提交按钮,测试效果。

    3. 创建页面:

    input.jsp

    <s:form action="avoid">
        <s:token></s:token>
        <s:textfield name="username" label="Enter your name"></s:textfield>
        <s:textfield name="birthday" label="Enter your birthday"></s:textfield>
        <s:submit value="submit"></s:submit>
    </s:form>

    要使用Struts 2的防止表单重复提交功能,需要在form标签中使用token标签,他会产生一个唯一的标识符,与其他参数一起提交到服务器,服务器会根据token标签所产生的标识符判断表单是否为重复提交的表单,这个功能是由Token拦截器完成的。

    error.jsp

    <body>
        do not duplicate submissions form!
    </body>

    当表单重复提交,Token拦截器会返回一个"invalid.token"结果,结果将页面转到这个页面,提示用户错误信息。

    output.jsp

    <body>
        Your Name : <s:property value="username"/>
        <br />
        Your Birthday : <s:property value="birthday"/>
    </body>

    若没有重复提交表单,那么就显示正确的页面。

    4. 测试

    在浏览器中输入:http://localhost:8081/AvoidDuplicateSubmissions/input.jsp,得到如下界面

    连续多次点击"submit"按钮,查看效果

    可以看到,token拦截器的设置生效了,他阻止了表单的重复提交,并给出了错误提示

    这次我们只点击一次提交(请重新输入URL,或后退到输入页面后刷新一下,这是因为token的标示在提交一次后已被修改,不刷新标示符是不可能与服务器存留的标示符一致的)

    可以看到,表单被正确的处理了。

    对于一次正常提交和一次重复提交,使用token拦截器会使得浏览器最终重定向到invalid.token指定的Result。那么,有没有更好的办法可以让“重复提交”看起来好像没有发生过,浏览器最终跳转到正常提交指定的Result呢?

           当然有,而且做法同样简单,把token拦截器换为tokenSession拦截器即可。tokenSession拦截器与token拦截器唯一的不同是在判断某个请求为重复请求之后,并不是立即重定向到名为invalid.token的Result,而是先阻塞这个重复请求,直到浏览器响应最初的正常请求,然后就可以跳转到处理正常请求后的Result了。

    <struts>
        <package name="avoidPackage" extends="struts-default">
            <action name="avoid" class="struts2.action.AvoidAction">
                <interceptor-ref name="tokenSession"/>  
                <interceptor-ref name="defaultStack"></interceptor-ref> 
                <result name="success">/output.jsp</result>
            </action>
        </package>
    </struts>

     原博客:http://blog.csdn.net/a672489861/article/details/9851737  http://www.cnblogs.com/suxiaolei/archive/2011/11/03/2234408.html

  • 相关阅读:
    关于read函数的一些分析
    条件变量
    epoll的边缘触发与水平触发
    内核态的接收缓冲区和发送缓冲区
    SourceTreet提交时显示remote: Incorrect username or password ( access token )(4种解决办法)
    前端技术汇总+Vue最新快速上手
    MyBatisPlus性能分析插件,条件构造器,代码自动生成器详解
    博客园怎样在Markdown编辑模式下调整图片大小(已解决)
    MyBatisPlus分页查询,删除操作
    idea括号选中时出现一条下滑线(突出显示)打开或关闭方法
  • 原文地址:https://www.cnblogs.com/fengmingyue/p/6159939.html
Copyright © 2020-2023  润新知