1. 程式人生 > >表單二次重複提交的問題

表單二次重複提交的問題

如果網速比較慢的情況下,使用者點選的提交發現半天沒有反映,於是又重新點選了幾次提交按鈕,這就造成了重複提交的問題。那麼在我們的開放中必須解決這種重複提交的問題。比如有個需要使用者填寫使用者名稱和密碼然後提交到後臺進行登入驗證的一個提交,重複提交主要體現如下幾種場景:

1、場景一:在網路延遲的情況下讓使用者有時間點選多次submit按鈕導致表單重複提交。

在網路比較慢的情況下,使用者連續快速的點選多次提交按鈕。

2、場景二:表單提交後用戶點選【重新整理】按鈕導致表單重複提交。

使用者點選了提交按鈕,然後點選瀏覽器上的【重新整理】按鈕對form表單又進行一次提交。

3、場景三:使用者提交表單後,點選瀏覽器的【後退】按鈕回退到表單頁面後進行再次提交

防止使用者重複提交主要有如下幾種解決方案:

解決方案一:利用JavaScript防止表單重複提交。
在客戶端的js程式碼中設定一個標識位,第一次提交後將標誌位設定成true。js程式碼如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
  <!DOCTYPE HTML>
  <html>
    <head>
      <title>Form表單</title>
          <script type="text/javascript">
          var isCommitted = false;//表單是否已經提交標識,預設為false
          function dosubmit(){
              if(isCommitted==false){
                 isCommitted = true;//提交表單後,將表單是否已經提交標識設定為true
                 return true;//返回true讓表單正常提交
             }else{
                 return false;//返回false那麼表單將不提交
             }
         }
     </script>
   </head>
   
   <body>
       <form action="${pageContext.request.contextPath}/servlet/DoFormServlet" onsubmit="return dosubmit()" method="post">
         使用者名稱:<input type="text" name="username">
         <input type="submit" value="提交" id="submit">
     </form>
   </body>
 </html>

這種方法是使用者第二次點選提交按鈕時是不起作用的。除了用這種方式之外,經常見的另一種方式就是表單提交之後,將提交按鈕設定為不可用,讓使用者沒有機會點選第二次提交按鈕,程式碼如下:

function dosubmit(){
     //獲取表單提交按鈕
     var btnSubmit = document.getElementById("submit");
     //將表單提交按鈕設定為不可用,這樣就可以避免使用者再次點選提交按鈕
     btnSubmit.disabled= "disabled";
     //返回true讓表單可以正常提交
     return true;
 }

使用JavaScript防止表單重複提交的做法只對上述提交到導致表單重複提交的三種場景中的【場景一】有效,而對於【場景二】和【場景三】是沒有用,依然無法解決表單重複提交問題。

解決方案二:利用Session防止表單重複提交
對於【場景二】和【場景三】導致表單重複提交的問題,既然客戶端無法解決,那麼就在伺服器端解決,在伺服器端解決就需要用到session了。

具體的做法:
1、獲取使用者填寫使用者名稱和密碼的頁面時向後臺傳送一次請求,這時後臺會生成唯一的隨機標識號,專業術語稱為Token(令牌)。

2、將Token傳送到客戶端的Form表單中,在Form表單中使用隱藏域來儲存這個Token,表單提交的時候連同這個Token一起提交到伺服器端。

3、伺服器端判斷客戶端提交上來的Token與伺服器端生成的Token是否一致,如果不一致,那就是重複提交了,此時伺服器端就可以不處理重複提交的表單。如果相同則處理表單提交,處理完後清除當前使用者的Session域中儲存的標識號。

看具體的範例:

1.建立FormServlet,用於生成Token(令牌)和跳轉到form.jsp頁面

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
  public class FormServlet extends HttpServlet {
    private static final long serialVersionUID = -884689940866074733L;
 
    public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
 
        String token =  UUID.randomUUID().toString() ;//建立令牌
        System.out.println("在FormServlet中生成的token:"+token);
         request.getSession().setAttribute("token", token);  //在伺服器使用session儲存token(令牌)
         request.getRequestDispatcher("/form.jsp").forward(request, response);//跳轉到form.jsp頁面
     }
 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         doGet(request, response);
     }
 
}

2.在form.jsp中使用隱藏域來儲存Token(令牌)

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  <html>
  <head>
  <title>form表單</title>
  </head>
  
  <body>
      <form action="${pageContext.request.contextPath}/servlet/DoFormServlet" method="post">
         <%--使用隱藏域儲存生成的token--%>
         <%--
             <input type="hidden" name="token" value="<%=session.getAttribute("token") %>">
         --%>
         <%--使用EL表示式取出儲存在session中的token--%>
         <input type="hidden" name="token" value="${token}"/> 
         使用者名稱:<input type="text" name="username"> 
         <input type="submit" value="提交">
     </form>
 </body>
 </html>

3.DoFormServlet處理表單提交

import java.io.IOException;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  
  public class DoFormServlet extends HttpServlet {
 
     public void doGet(HttpServletRequest request, HttpServletResponse response)
                 throws ServletException, IOException {
 
             boolean b = isRepeatSubmit(request);//判斷使用者是否是重複提交
             if(b==true){
                 System.out.println("請不要重複提交");
                 return;
             }
             request.getSession().removeAttribute("token");//移除session中的token
             System.out.println("處理使用者提交請求!!");
         }
         
         /**
          * 判斷客戶端提交上來的令牌和伺服器端生成的令牌是否一致
          * @param request
          * @return 
          *         true 使用者重複提交了表單 
          *         false 使用者沒有重複提交表單
          */
         private boolean isRepeatSubmit(HttpServletRequest request) {
             String client_token = request.getParameter("token");
             //1、如果使用者提交的表單資料中沒有token,則使用者是重複提交了表單
             if(client_token==null){
                 return true;
             }
             //取出儲存在Session中的token
             String server_token = (String) request.getSession().getAttribute("token");
             //2、如果當前使用者的Session中不存在Token(令牌),則使用者是重複提交了表單
             if(server_token==null){
                return true;
             }
             //3、儲存在Session中的Token(令牌)與表單提交的Token(令牌)不同,則使用者是重複提交了表單
             if(!client_token.equals(server_token)){
                 return true;
             }
             
             return false;
         }
 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         doGet(request, response);
     }
 
 }

通過這種方式處理表單重複提交,可以解決上述的場景二和場景三中出現的表單重複提交問題。
在實際的專案中一般前臺ui和後臺都要防止重複提交。



作者:劉阿航
連結:https://www.jianshu.com/p/01b6ab61f24a
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。