Summary of methods to prevent Web forms from submitting repeatedly

Keywords: Java Session JSP Javascript

In Web development, it is a common task to deal with form re submission. So, what are the scenarios that lead to repeated submission of forms? What's the problem with duplicate forms? What are some ways to avoid duplicate forms?

Scenario of repeated form submission

1. Scenario 1: the server fails to respond to the results in time (network delay, concurrent queuing and other factors), resulting in the front-end page not refreshing in time, and the user has the opportunity to submit the form multiple times.

2. Scenario 2: after the form is submitted successfully, the user clicks the refresh button again to cause the form to be submitted repeatedly.

3. Scenario 3: after the form is submitted successfully, click the back button to return to the form page and submit again.

The disadvantages of form repeated submission

This is illustrated by a simple example.

  • Form page: test-form-submit-repeat.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Process form resubmission</title>
</head>
<body>

    <form action="<%=request.getContextPath()%>/formServlet.do" method="post">
        //Name: < input type = "text" name = "username" / >
        <input type="submit" value="Submission">
    </form>
</body>
</html>
  • Background servlet: FormServlet.java
public class FormServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        String userName = req.getParameter("username");

        try {
            // Slow processing efficiency of simulation server
            Thread.sleep(3 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Insert data:"   userName);
    }
}

Submit results repeatedly in the experiment form:

Obviously, from the results of the demonstration, if the form is submitted repeatedly, the same data will be repeatedly inserted into the database. In fact, this should not happen.

How to avoid submitting forms repeatedly

There are two ways to solve the problem of form re submission: blocking at the front end and blocking at the server end.

1. Block the repeated submission of forms in the front end

Multiple methods can be used to block the repeated submission of forms in the front end:
(1) intercept by setting variable flag

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Process form resubmission</title>
</head>
<body>
    <form action="<%=request.getContextPath()%>/formServlet.do" method="post" onsubmit="return checkSubmit();">
        //Name: < input type = "text" name = "username" / >
        <input type="submit" value="Submission">
    </form>
</body>
<script type="text/javascript">
    // Set form submission flag
    var submit = false;
    function checkSubmit() {
        if(!submit) {
            // Set flag after form submission
            submit = true;
            return true;
        }
        // The form has been submitted. It is not allowed to submit again
        console.log("Please do not submit the form again!");
        return false;
    }
</script>
</html>

(2) intercept by disable button
In addition to setting flag bits in the front end to intercept, you can also disable the button after the form is submitted, which completely prevents the possibility of the form being submitted repeatedly.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Process form resubmission</title>
</head>
<body>
    <form action="<%=request.getContextPath()%>/formServlet.do" method="post" onsubmit="return disabledSubmit();">
        Full name:<input type="text" name="username" />
        <input id="submitBtn" type="submit" value="Submission">
    </form>
</body>
<script type="text/javascript">
    function disabledSubmit() {
        // Disable after the first execution of the submit button to avoid repeated submission
        document.getElementById("submitBtn").disabled= "disabled";
        return true;
    }
</script>
</html>

Of course, you can also hide the button directly after submitting a submission. However, whether it is necessary to do so needs to consider whether the user's operating experience is acceptable.

Although blocking at the front end can solve the problem of repeated submission of form in scenario I, there is no way to solve the problem of repeated submission of form in scenario II (refresh) and scenario III (back to resubmit).

2. Block the repeated submission of forms on the server side
In fact, intercepting the repeatedly submitted request of the form on the server side is realized by saving a token on the server side, and the token saved on the server side needs to be passed through the front end, which is divided into three steps:

Step 1: save a random token in the server when visiting the page

public class FormServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        UUID uuid = UUID.randomUUID();
        String token = uuid.toString().replaceAll("-", "");
        // Randomly generate a token when visiting the page and save it in the server session
        req.getSession().setAttribute("token", token);
        req.getRequestDispatcher("/test-form-submit-repeat.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doGet(req, resp);
    }
}

Random token s can be generated in any appropriate way, here by UUID.

Step 2: pass the random token saved by the server through the front page

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Process form resubmission</title>
</head>
<body>
    <form action="<%=request.getContextPath()%>/doFormServlet.do" method="post">
        <!-- Hide domain save server token -->
        <input type="hidden" name="token" value="<%=session.getAttribute("token")%>" />
        //Name: < input type = "text" name = "username" / >
        <input id="submitBtn" type="submit" value="Submission">
    </form>
</body>
</html>

Step 3: when submitting a form, the server checks the token to determine whether it is a repeatedly submitted form request.

public class DoFormServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");

        if(checkRepeatSubmit(req)) {
            System.out.println("Please do not submit again!");
            return;
        }
        
        // It is very important to clear the token after the first processing of the form.
        req.getSession().removeAttribute("token");

        String userName = req.getParameter("username");
        try {
            // Slow processing efficiency of simulation server
            Thread.sleep(3 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Insert data:"   userName);
    }

    // Check whether the form is submitted repeatedly
    private boolean checkRepeatSubmit(HttpServletRequest req) {
        Object sessionTokenObj = req.getSession().getAttribute("token");
        if(sessionTokenObj == null) {
            // Form resubmission
            System.out.println("Session token is NULL!");
            return true;
        }

        String paramToken = req.getParameter("token");
        if(paramToken == null) {
            // Illegal request
            System.out.println("Parameter token is NULL!");
            return true;
        }

        if(!paramToken.equals(sessionTokenObj.toString())) {
            // Illegal request
            System.out.println("Token not same");
            return true;
        }
        return false;
    }
}

Obviously, it's very effective to block the repeated submission of scenario 2 and scenario 3 forms by saving token s on the server. Moreover, this method can also block the repeated submission of scenario I forms.

That is to say, the ultimate solution for blocking the repeated submission of forms is to block it on the server side! However, considering the user experience, it may be necessary to intercept at the front end at the same time, which can be determined according to the specific product design.

In addition, it's interesting: in the latest Firefox browsing Version (Firefox Quantum 59.0.1 64 bit), the browser can handle the repeated submission of the form in scenario I (but can't handle the repeated submission of the form in scenario II and scenario III).

After verification, this function is not available in the latest version of Chrome (Chrome 65.0.3325.181).


For more professional front-end knowledge, please go to [ape 2048] www.mk2048.com

Posted by jwinn on Sat, 26 Oct 2019 23:44:14 -0700