重複コミットの問題の解決(フロントエンドとバックエンドのソリューションjava版)


1.フォームの重複コミットの問題はなぜ発生しますか?
  • ネットワーク遅延の場合、ユーザがsubmitボタンを複数回クリックすると、フォームが
  • に再送信される.
  • ユーザーがフォームを提出した後、「更新」ボタンをクリックするとフォームが繰り返し提出される(ブラウザのリフレッシュボタンをクリックすると、ブラウザが最後にしたことをもう一度やることになる.これによってフォームが繰り返し提出されることになるからだ)
  • .
  • ユーザーがフォームを提出した後、ブラウザの【戻る】ボタンをクリックしてフォームページに戻り、
  • を再度提出する.
    2.ソリューション
    2.1フロントエンドソリューション(根本的な解決策がない)
    2.1.1 JavaScriptでFormフォームを制御するのは1回のみです
    主なコード:
    <form action="${pageContext.request.contextPath}/servlet/DoFormServlet" οnsubmit="return dosubmit()" method="post"><input type="text" name="username">
        <input type="submit" value="  " id="submit">
    </form>
    
    <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>
    
    

    2.1.2コミット後にコミットボタンを使用不可に設定します(体験があまりよくないかもしれません)
    主なコード:
    function dosubmit(){
        //        
        var btnSubmit = document.getElementById("submit");
        //             ,                 
        btnSubmit.disabled= "disabled";
        //  true         
        return true;
    }
    
    

    2.1.3コミット後にページを閉じる(ユーザ体験が悪い場合がある)
    2.1.4提出後にページデータをリフレッシュするこの時点で本条に記録されているプライマリ・キーIDを取得し、バックエンドで挿入する際にプライマリ・キーidが存在するかどうかを確認して挿入または修正する
    2.2バックエンドソリューション
    2.2.1 Sessionによるフォームの重複コミットの防止
    主な手順:
  • サーバ側で一意のランダム識別番号が生成され、専門用語はToken(トークン)と呼ばれ、現在のユーザのSessionドメインにこのToken
  • が保存される.
    token生成クラス
    public class TokenProccessor {
     
        /*
         *      (              )
         *1、         
         *2、          
         *3、           ,      
         */
        private TokenProccessor(){
        }
        
        private static final TokenProccessor instance = new TokenProccessor();
        
        /**
         *       
         * @return
         */
        public static TokenProccessor getInstance(){
            return instance;
        }
        
        /**
         *   Token
         * Token:Nv6RRuGEVvmGjB+jimI/gw==
         * @return
         */
        public String makeToken(){
            String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";
            //       128     16     md5
            try {
                MessageDigest md = MessageDigest.getInstance("md5");
                byte md5[] =  md.digest(token.getBytes());
                //base64  --              adfsdfsdfsf
                BASE64Encoder encoder = new BASE64Encoder();
                return encoder.encode(md5);
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
    
    String token = TokenProccessor.getInstance().makeToken();//    
    System.out.println(" FormServlet    token:"+token);
    request.getSession().setAttribute("token", token);  //      session  token(  )
    request.getRequestDispatcher("/form.jsp").forward(request, response);//   form.jsp      
    
  • は、TokenをクライアントのFormフォームに送信し、Formフォームに非表示ドメインを使用してこのTokenを格納し、フォームのコミット時にこのTokenとともにサーバ側
  • にコミットする.
    <form action="${pageContext.request.contextPath}/servlet/DoFormServlet" method="post">
        <%--          token--%>
        <%--
            <input type="hidden" name="token" value="token") %>">
        --%>
        <%--  EL        session  token--%>
        <input type="hidden" name="token" value="${token}"/><input type="text" name="username"> 
        <input type="submit" value="  ">
    </form>
    
  • サーバ側でクライアントから送信されたTokenがサーバ側で生成されたTokenと一致しているか否かを判断し、一致しない場合は重複送信であり、このときサーバ側は重複送信のフォームを処理しなくてもよい.同じ場合は処理フォームが発行され、処理が完了すると現在のユーザのセッションドメインに格納されている識別番号が消去されます.
  • /**
     *                           
     * @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;
    }
    

    doGetメソッド:
    
    boolean b = isRepeatSubmit(request);//           
    if(b==true){
        System.out.println("       ");
        return;
    }
    request.getSession().removeAttribute("token");//  session  token
    System.out.println("        !!");
    

    【シーン2】と【シーン3】でフォームが重複してコミットされる問題は、クライアントが解決できない以上、サーバ側で解決し、サーバ側で解決するにはセッションが必要です.tokenの方法で【シーン2】と【シーン3】の状況を解決できます.
    2.3フロントエンドとバックエンドのソリューション
    構想:フロントエンドが提出された後、ページにアニメーションをロードし、バックエンド処理が完了した後、本条データのプライマリキーidをフロントエンドに戻し、フロントエンドが2回目の要求時にプライマリキーidに値を添付すればよい.
    まとめ:
    一般的な方法は4つあります.
  • メソッド1:「コミット(登録)」を無効にするボタン、ユーザーが初めて登録ボタンをクリックした時、事務はすでに提出したが、ネットは不安定で、タイムリーに応答していないかもしれない.この時、ユーザーは提出ボタンをクリックしていないと思っているかもしれないし、さらに提出ボタンを再度クリックするかもしれない.これは私たちの業務論理に合わないので、私たちはユーザーが初めて提出ボタンをクリックした後、すぐにコミットボタンを使用不可に設定します(たとえば、グレーになります).ユーザーは再コミットしません.
  • 方法2:ページリダイレクトを採用し、ユーザーがコミットボタンをクリックした後、新しいページに移動し、ユーザーにコミット成功を提示する.
  • メソッド3:フラグを使用して、1つのセッションでユーザーがフォームを要求すると、サーバ側はページを送信する際に一連の暗号文(乱数でもよい)を生成し、ページに従って同時にクライアントに送ることができ、クライアントがフォームに記入した後、提出ボタンをクリックし、再びこの暗号文をサーバ側に送り、サーバ側の暗号文と比較し、同じであれば業務の処理を開始し、異なる場合は今回の業務を処理しない(インターネット金融業界の多くはこのような方式を採用している).
  • メソッド4:サーバ側のデータベースを使用すると、データベースのテーブルに関連する制約を追加して重複コミットを防止できますが、データベースの負荷が増加します.

  • 上記の4つの方法はそれぞれメリットとデメリットがあり、プロジェクトの実際の状況に応じて選択したり、これらの方法を組み合わせて使用したりすることができます.
    参照リンク;https://www.jianshu.com/p/5fd07359ad22 https://www.cnblogs.com/guozhenqiang/p/5439455.html