【问题标题】:Prevent Multiple Submit Button Clicks Without Using JavaScript or JQuery in Web App?防止在 Web 应用程序中不使用 JavaScript 或 JQuery 的情况下多次单击提交按钮?
【发布时间】:2025-12-09 18:20:03
【问题描述】:

我的简单网络应用程序如下:我有一个 JSP 表单 (MyForm.JSP),它接受用户输入并将其传递给我的第一个 servlet ("/myfirstservlet")。

这个 servlet 处理用户输入值到我的Fruits 表的 SQL 插入,然后将用户重定向到我的结果 servlet ("/results")。

我的结果 servlet 然后检查 "ADD" 参数,如果“真”(即它等于 "success")它最终将用户重定向到我的结果 JSP(Results.JSP),它存储在路径中:@ 987654328@.

我的 JSP 表单 (MyForm.JSP) 也存储在路径内:WEB-INF/MyFolder/MyForm.jsp

我这样做是为了防止用户通过单击结果 JSP 页面上的刷新按钮重新提交表单,以避免之前刚刚输入数据库的相同数据的多个条目。

我现在的问题是:如何防止用户在我的表单 (MyForm.JSP) 上单击提交按钮 多次 次,从而防止多行相同的数据进入我的数据库 不使用 使用 JavaScript 或 JQuery?

基本上,我想在我的服务器而不是客户端验证表单仅提交一次。

我的 JSP 表单 (MyForm.JSP):

<form action="myfirstservlet" do="POST">
   <input type="text" name="fruit"><br>
   <input type="text" name="color"><br>
   <input type="submit" value="Submit">
</form>

我的第一个 servlet ("/myfirstservlet"):

protected void doPost(...){
   String fruit = request.getParameter("fruit");
   String color = request.getParameter("color");

   String sql = "INSERT INTO fruits (fruit, color) VALUES" + "(\"" + fruit +  "\", \""  + color +  "\");";

   utilitySQL.sqlInsert(sql); // My utility class that handles sql inserts

   response.sendRedirect("results?ADD=SUCCESS");
}

我的结果 servlet ("/results"):

protected void doPost(...){

   response.setContentType("text/html");    

   if (request.getParameter("ADD").equals("SUCCESS"))
      request.getRequestDispatcher("WEB-INF/MyFolder/Results.jsp").forward(request, response);

}

我的结果 JSP (Results.JSP):

<body>
<h1>Results JSP</h1>


  //Reads data from MySQL database and prints it as an Array List.

</body>

编辑:在我的第一个 servlet 中修复了我准备好的语句:

protected void doPost(...){


       String fruit = request.getParameter("fruit");
       String color = request.getParameter("color");

       try 
        {
            String sql2 = "INSERT INTO practice (fruit, color) VALUES (?, ?);";
            Connection connect = SQLHelperClass.connectOnly();
            PreparedStatement pstmt;
            pstmt = connect.prepareStatement(sql2);
            pstmt.setString(1, fruit);
            pstmt.setString(2, color);

            pstmt.execute();

            response.sendRedirect("results?ADD=success");
        } 

        catch (SQLException e) 
        {           
            e.printStackTrace();
        }

}

【问题讨论】:

  • 你考虑&lt;input type="submit" onclick="this.disabled=true" value="Submit"&gt;javascript吗?
  • 我读到最好在服务器中验证
  • 好的,但你能告诉我你在验证什么吗?
  • 服务器是否已接收到带有用户输入数据的表单,如果为true,则阻止来自具有相同数据的表单的其他多次提交点击。基本上,这是为了确保每次填写和提交的每个表单一次只插入一行。
  • 通常在创建 Web 表单时,通过引导用户获得所需的用户体验来引导用户行为是一种很好的做法。禁用按钮或显示流程覆盖是避免多次提交的常用方法。如果你想做这个服务器端,想想你是否希望后端 api 限制多个POST 请求?如果是这样,数据库完整性或速率限制可能是不错的选择。

标签: java mysql jsp web-applications data-entry


【解决方案1】:

如果您有一个登录用户的 id 字段,这会更容易,因为您可以为特定用户提交的结果创建一个表,然后在将其输入到 fruits 表之前,检查用户是否已经提交相同的数据。

从外观上看,您似乎没有任何用户标识字段,因此防止重复的一种巧妙方法可能是使用会话。

会话对于当前使用您的应用程序/网站的用户来说是唯一的。每个访问您的网站/应用程序的人都会获得他们自己唯一的会话 ID。 (它们被存储为 cookie)

例如:

protected void doPost(...){
   String fruit = request.getParameter("fruit");
   String color = request.getParameter("color");

   //unless you wanna complicate things, i would create a string out of the two parameters and store it into an arraylist of strings
   String value = fruit+color; 

   HttpSession session = (request.getSession()); //get session
   if(null == session.getAttribute("duplicates")){ //if session variable empty then we know that user has not submitted anything yet so we let them insert into db

     insertFruit(fruit,color); //add to db

     ArrayList<String> duplicates = new ArrayList<String>(); //create arraylist
     duplicates.add(value); //add our unique value
     session.setAttribute("duplicates", duplicates); //set as session variable

    }else{
     //here the session variable is not empty so that means the user has already submitted something so lets check the arraylist and make sure the value does not already exist

     ArrayList<String> duplicates = (ArrayList<String>) session.getAttribute("duplicates");

     if(!duplicates.contains(value)){
      //if arraylist does not contain the same value, then it's safe to add
       insertFruit(fruit,color); //add to db

      //forgot this part
      duplicates.add(value);
      session.setAttribute("duplicates", duplicates); //update the variable
     }


    }


   response.sendRedirect("results?ADD=SUCCESS");
}

public void insertFruit(String fruit, String color){

       try(Connection connect = SQLHelperClass.connectOnly()){
         PreparedStatement pst = connect.prepareStatement("INSERT INTO practice (fruit, color) VALUES (?, ?);");

        pst.setString(1, fruit);
        pst.setString(2, color);

        pst.executeUpdate();

          }catch (SQLException e) {
            e.printStackTrace();
          }

}

编辑 1:

关于不为每个 servlet 重复数据库操作的评论。您需要分离出逻辑。人们通常这样做的方式是为所有数据库操作创建一个单独的类。

例如...

创建一个名为FruitDao 的类,在这里保存所有与水果相关的数据库操作

公开课 FruitDao{

public void insertFruit(String fruit, String color){

       try(Connection connect = SQLHelperClass.connectOnly()){
         PreparedStatement pst = connect.prepareStatement("INSERT INTO practice (fruit, color) VALUES (?, ?);");

        pst.setString(1, fruit);
        pst.setString(2, color);

        pst.executeUpdate();

          }catch (SQLException e) {
            e.printStackTrace();
          }

}

要从您的 servlet 调用它,只需执行以下操作:

protected void doPost(...){
   FruitDao fdao = new FruitDao(); // get the db class for fruits
   String fruit = request.getParameter("fruit");
   String color = request.getParameter("color");

   //unless you wanna complicate things, i would create a string out of the two parameters and store it into an arraylist of strings
   String value = fruit+color; 

   HttpSession session = (request.getSession()); //get session
   if(null == session.getAttribute("duplicates")){ //if session variable empty then we know that user has not submitted anything yet so we let them insert into db

     fdao.insertFruit(fruit,color); //add to db

     ArrayList<String> duplicates = new ArrayList<String>(); //create arraylist
     duplicates.add(value); //add our unique value
     session.setAttribute("duplicates", duplicates); //set as session variable

    }else{
     //here the session variable is not empty so that means the user has already submitted something so lets check the arraylist and make sure the value does not already exist

     ArrayList<String> duplicates = (ArrayList<String>) session.getAttribute("duplicates");

     if(!duplicates.contains(value)){
      //if arraylist does not contain the same value, then it's safe to add
        fdao.insertFruit(fruit,color); //add to db

      //forgot this part
      duplicates.add(value);
      session.setAttribute("duplicates", duplicates); //update the variable
     }


    }


   response.sendRedirect("results?ADD=SUCCESS");
}

【讨论】:

  • 这里需要注意的是,如果用户删除了他们的 ssid cookie,他们将能够提交相同的数据
  • 还检查了 insertFruit 方法,这就是你应该如何连接到你的数据库。通过将其包装在 try catch 块中,它会自动为您关闭连接。
  • session.setAttributes() 需要一个对象作为第二个参数。
  • 我的数据库连接 SQLHelperClass 有另一个外部类,它有一个方法 connectOnly(),它返回一个 Connect 对象。这种方式比为我拥有的每个 servlet 重复编写它更不理想吗?
  • @JaeBin setAttribute 不是 setAttributes
最近更新 更多