页面重复提交的解决办法

        任何项目里面都要处理这个问题,  我觉得客户端和服务端都做处理才合适.

        客户端这边, 一般情况下, 浏览器都会缓存已经访问过的页面, 在浏览器第一次请求时, 返回的响应消息中的Last-Modified头字段可用于指定响应内容的最后更新时间,当客户机缓存此文档内容后,它在以后的请求消息中将根据Last-Modified头字段指定的时间来生成If-Modified-Since请求头字段,以指出缓存文档的最后更新时间。只有文档的修改时间比If-Modified-Since请求头指定的时间新时,服务器才会返回文档内容。如果自从If-Modified-Since指定的时间以来,网页内容没有发生修改,服务器将返回一个304(Not Modified)状态码来表示浏览器缓存的版本是最新的,而不会向浏览器返回文档内容,浏览器则继续使用以前缓存的内容。通过这种方式,可以在一定程度上减少浏览器与服务器之间的通信数据量,从而提高了通信效率。
         说简单点就是要清除缓存. 就三句代码
<%
response.setHeader("Expires","0");
response.setHeader("Cache-Control","no-store");
response.setHeader("Pragrma","no-cache");
%>


        服务器端, 若项目用到了struts, 那么struts的令牌就是很好的解决办法. 在提交的jsp以及该jsp的前后action中这样处理, 提交前的action中 

        saveToken(request); 

提交后的action中  if (isTokenValid(request, true)) {                                  
                               //执行提交操作 
                        }else {
                                 // 重复提交  
                        }
就这几句代码就行, 但由前action到jsp 和jsp到后action显然是两次对话, 用request是怎么判断的, 这是我刚接触令牌的时候就不解的问题, 后来考虑想必是在jsp页面上做了处理, 保存了这个相关的变量又传入后action的, 像struts早有鸡肋一说, 也许某天就会退出这个舞台, 也许以后得用jsp/servlet自己写框架, 研究了下令牌机制的源代码.

在org.apache.struts.action.action中
protected void saveToken(HttpServletRequest request) {
      token.saveToken(request);
}
这里的token是org.apache.struts.util.TokenProcessor 的对象
具体的实现是在它的saveToken方法下
public synchronized void saveToken(HttpServletRequest request) {
        HttpSession session = request.getSession();
        String token = generateToken(request);

        if (token != null) {
            session.setAttribute(Globals.TRANSACTION_TOKEN_KEY, token);
        }
}
这里的Globals.TRANSACTION_TOKEN_KEY是一个常量"org.apache.struts.action.TOKEN"
这里的generateToken(request)是token的另一方法
public synchronized String generateToken(HttpServletRequest request) {
        HttpSession session = request.getSession();
        return generateToken(session.getId());
}
而在generateToken(session.getId())这个方法中主要是对id进行了加密处理
然后把这个加密后的字符串放入session中.

在jsp页面中则必须使用struts的form标签, 因为在调用标签FormTag类时, 有个方法
protected String renderToken() {
    StringBuffer results = new StringBuffer();
    HttpSession session = pageContext.getSession();

    if (session != null) {
        String token =
            (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
           
        if (token != null) {
            results.append("<input type=/"hidden/" name=/"");
            results.append(Constants.TOKEN_KEY);
            results.append("/" value=/"");
            results.append(token);
            if (this.isXhtml()) {
                results.append("/" />");
            } else {
                results.append("/">");
            }
        }
    }

    return results.toString();
}
当检测到session中的Globals.TRANSACTION_TOKEN_KEY不为空时,在jsp页面创建元素
<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="">
这个可以查看生成的jsp页面的html代码, 会发行紧跟着form标签下就是这个hidden.

在其后的action中isTokenValid方法,
也是调用TokenProcessor类中的isTokenValid方法
public synchronized boolean isTokenValid(
    HttpServletRequest request,  boolean reset) {
    // Retrieve the current session for this request
    HttpSession session = request.getSession(false);
    if (session == null) {
        return false;
    }

    // Retrieve the transaction token from this session, and
    // reset it if requested
    /** 在这里取得了令牌的值 */
    String saved = (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
   
    if (saved == null) {
        return false;
    }
 
    /** 这里将令牌销毁 */
    if (reset) {
        this.resetToken(request);
    }
  
    /** 从jsp页面hidden域中获得值 */  
    String token = request.getParameter(Constants.TOKEN_KEY);

    if (token == null) {
        return false;
    }
   
    /** 进行比较 */
    return saved.equals(token);
}
resetToken方法
public synchronized void resetToken(HttpServletRequest request) {
  HttpSession session = request.getSession(false);
  if (session == null) {
      return;
  }

  session.removeAttribute(Globals.TRANSACTION_TOKEN_KEY);
}

其实自己完成令牌功能也相当简单

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值