防止表格重新提交

Posted

技术标签:

【中文标题】防止表格重新提交【英文标题】:Prevent Form Resubmit 【发布时间】:2018-03-18 19:31:14 【问题描述】:

几周以来我一直在寻找这个答案,但许多其他问题/答案不适合我的项目,或者根本不起作用。

让我解释一下我的项目。 这是一个简单的查看页面,在页面加载时它将文本设置为整个页面的不同字段。在底部,我有一个带有按钮的容器,用于控制在容器中显示哪个 div。每次单击按钮时,我都会运行一些代码来将数据源设置为网格视图或用文本填充字段。我注意到当我单击 F5 刷新页面时,我得到了那个讨厌的“表单重新提交”弹出窗口,这将导致最后一次单击按钮,例如将注释附加到列表视图被复制。

这里有一些代码可以理解我在做什么

HTML(不完整,只是理解我的问题所必需的)

<td style="vertical-align: top;" class="Flow-Sub-Menu">
    <asp:Button ID="Button_Sub_Menu_Financial" runat="server" Text="Financial Info" OnClick="Button_Sub_Menu_Financial_Click" />
    <asp:Button ID="Button_Sub_Menu_Indemnitors" runat="server" Text="Indemnitors" OnClick="Button_Sub_Menu_Indemnitors_Click" />
    <asp:Button ID="Button_Sub_Menu_Court" runat="server" Text="Court Info" OnClick="Button_Sub_Menu_Court_Click" />
    <asp:Button ID="Button_Sub_Menu_Forfeiture" runat="server" Text="Forfeiture Info" OnClick="Button_Sub_Menu_Forfeiture_Click" />
    <asp:Button ID="Button_Sub_Menu_Collaterals" runat="server" Text="Collaterals" OnClick="Button_Sub_Menu_Collaterals_Click" />
    <asp:Button ID="Button_Sub_Menu_Notes" runat="server" Text="Notes" OnClick="Button_Sub_Menu_Notes_Click" />
    <asp:Button ID="Button_Sub_Menu_Documents" runat="server" Text="Documents" OnClick="Button_Sub_Menu_Documents_Click" />
</td>

<div id="Div_Sub_Menu_Notes" runat="server" visible="false">
    <div class="row">
        <div class="col-xs-4" style="min-width: 550px;">
            <div class="Flow-Box">
                <div class="Flow-Box-Content" style="height: 440px; overflow-y: scroll;">
                    <asp:ListView ID="LV_Notes" runat="server" ItemPlaceholderID="ItemPlaceHolder" DataKeyNames="Bond_Note_ID" OnPagePropertiesChanging="LV_Notes_PagePropertiesChanging" OnItemCommand="LV_Notes_ItemCommand">
                        <LayoutTemplate>
                            <table style="width: 500px; background-color: white;">
                                <tr id="ItemPlaceHolder" runat="server"></tr>
                                <tr>
                                    <td colspan="2">
                                        <asp:DataPager ID="DataPager_Notes" runat="server" PagedControlID="LV_Notes" PageSize="3">
                                            <Fields>
                                                <asp:NextPreviousPagerField ButtonType="Link" ShowFirstPageButton="false" ShowPreviousPageButton="true"
                                                   ShowNextPageButton="false" />
                                                <asp:NumericPagerField ButtonType="Link" />
                                                <asp:NextPreviousPagerField ButtonType="Link" ShowNextPageButton="true" ShowLastPageButton="false" ShowPreviousPageButton="false" />
                                            </Fields>
                                        </asp:DataPager>
                                    </td>
                                </tr>
                            </table>
                        </LayoutTemplate>
                        <ItemTemplate>
                            <tr>
                                <td colspan="2" style="padding-left: 10px; padding-right: 10px;">
                                    <div style="border: 2px solid grey; height: 75px; padding: 5px;">
                                        <%# Eval("Note") %>
                                    </div>
                                </td>
                            </tr>
                            <tr>
                                <td style="padding-left: 10px;" <%# (Eval("Document_ID").ToString() != "" ?  "" : "hidden=\"hidden\"") %>>
                                    <label>Document Attached : <%# Eval("Document_Title").ToString() %></label>
                                </td>
                                <td style="text-align: right; padding-right: 10px;" <%# (Eval("Document_ID").ToString() != "" ?  "" : "hidden=\"hidden\"") %>>
                                    <asp:Button ID="Button_View_Document" runat="server" Text="View/Download Document" CommandName="View_Document" CommandArgument='<%#Eval("Document_ID")%>' />
                                </td>
                            </tr>
                            <tr>
                                <td style="text-align: left; padding-left: 10px;">
                                    <div>
                                        <%# Convert.ToDateTime(Eval("Created_DateTime")).ToShortDateString() + " " + Convert.ToDateTime(Eval("Created_DateTime")).ToShortTimeString() %>
                                    </div>
                                </td>
                                <td style="text-align: right; padding-right: 10px;">
                                    <div>
                                        <%# Eval("Full_Name") %>
                                    </div>
                                </td>
                            </tr>
                            <tr>
                                <td colspan="2" style="height: 20px; border-top: 4px solid #009900;"></td>
                            </tr>
                        </ItemTemplate>
                    </asp:ListView>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-4" style="min-width: 500px;">
                <div class="Flow-Box">
                    <div class="Flow-Box-Label">
                        New Note
                    </div>
                    <div class="Flow-Box-Content">
                        <table class="Flow-Box-Table">
                            <tr>
                                <td>
                                    <textarea id="TextArea_New_Note" runat="server" style="width: 400px; height: 150px;"></textarea>
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <asp:Button ID="Button_Append_New_Note" runat="server" OnClick="Button_Append_New_Note_Click" Text="Append Note" />
                                </td>
                            </tr>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

C# 代码隐藏

protected void Button_Sub_Menu_Notes_Click(object sender, EventArgs e)

    resetSubMenuButtons();
    Button_Sub_Menu_Notes.BackColor = System.Drawing.Color.Orange;
    Div_Sub_Menu_Notes.Visible = true;
    string bondID = Request.QueryString["BondID"];
    bindNotes(bondID);


protected void bindNotes(string bondID)

    DataTable dt = Common_Functions.GetData("SELECT * FROM View_Bond_Notes_With_Name WHERE Bond_ID='" + bondID + "' ORDER BY Created_DateTime DESC");
    LV_Notes.DataSource = dt;
    LV_Notes.DataBind();


protected void LV_Notes_PagePropertiesChanging(object sender, PagePropertiesChangingEventArgs e)

    (LV_Notes.FindControl("DataPager_Notes") as DataPager).SetPageProperties(e.StartRowIndex, e.MaximumRows, false);
    string bondID = Request.QueryString["BondID"];
    bindNotes(bondID);


protected void LV_Notes_ItemCommand(object sender, ListViewCommandEventArgs e)

    if (String.Equals(e.CommandName, "View_Document"))
    
        ListViewDataItem dataItem = (ListViewDataItem)e.Item;
        string documentID = e.CommandArgument.ToString();
        Session["BondDocumentID"] = documentID;
        Page.ClientScript.RegisterStartupScript(GetType(), "openDocument", "window.open('/FileServing/Bond_Document_Server.aspx');", true);
    


protected void Button_Append_New_Note_Click(object sender, EventArgs e)

    string bondID = Request.QueryString["BondID"];
    string UserID = Request.Cookies["UserID"].Value;
    string newNote = TextArea_New_Note.Value;
    if (String.IsNullOrWhiteSpace(newNote))
    
        return;
    
    cmd = new SqlCommand("INSERT INTO Bond_Notes " +
        "(Bond_ID, Created_DateTime, Created_By, Note) " +
        "VALUES " +
        "(@Bond_ID, @Created_DateTime, @Created_By, @Note)", conn);
    cmd.Parameters.AddWithValue("@Bond_ID", bondID);
    cmd.Parameters.AddWithValue("@Created_DateTime", DateTime.Now);
    cmd.Parameters.AddWithValue("@Created_By", UserID);
    cmd.Parameters.AddWithValue("@Note", (String.IsNullOrWhiteSpace(newNote) ? (object)DBNull.Value : newNote));
    conn.Open();
    cmd.ExecuteNonQuery();
    conn.Close();
    TextArea_New_Note.Value = "";
    bindNotes(bondID);

因此,当用户单击“Button_Append_New_Note”时,代码将触发运行 sql 命令,然后通过调用“bindNotes(bondID)”刷新 ListView。如果用户出于某种原因单击 F5 或单击刷新,他们将重新提交该表单并添加重复项。我已经阅读了许多重定向到同一页面的解决方案,这在我的情况下不起作用,因为 div 将再次被隐藏,我希望 Notes div 保持可见,以便用户可以在列表视图中看到他们的新注释。我也不想让 sql 检查来查找重复项并防止插入,因为我可能希望用户在其他时间选择插入重复项,但用户将手动输入重复项。

关于如何避免重新提交表单的任何建议或建议?

也请不要批评代码,我正在寻找答案,而不是关于为什么使用内联字符串添加构建 SQL 语句不好的讲座。我最终会纠正和优化这些错误。

如果您在其他地方找到了我的解决方案,请确保它适用于我的情况,因为我已经多次找到相同的问题,但不适用于我的情况。

提前致谢。

【问题讨论】:

asp:updatePanel 包裹您的asp:listView 并将trigers 设置为您的按钮。 【参考方案1】:

您可能知道,ASP.NET Web 窗体将 POST 请求发送回服务器,然后在同一请求中重新呈现 Page。这就是为什么我们在点击“重新加载”时看到表单重新提交消息的原因。

为了避免这个问题,我们应该采用许多 Web 应用程序使用的 post-then-redirect 模式。以下是简要概述:

    用户单击一个按钮,该按钮将表单作为 POST 请求提交。 服务器接收请求并保存数据。 服务器发出 redirect 响应以及任何需要的状态。 浏览器接收重定向并通过发送 GET 请求加载相应的页面。

因为我们将浏览器重定向到一个新的(或相同的)位置,这会清除上一个请求中的 POST 数据,因此我们可以点击“重新加载”而无需再次重新提交相同的数据。我们重定向到的 URL 应该只包含服务器解释页面上显示的内容所需的数据。这种模式提供了一种持久的方式来维护 HTTP 中请求之间的状态。

要将此应用于问题中的 Web 表单,我们需要更改有关页面加载方式的一些假设。我们不会在运行每个 POST 操作后将注释绑定到页面,而是先重定向用户,然后再绑定注释:

protected void Button_Append_New_Note_Click(object sender, EventArgs e)

    string bondID = Request.QueryString["BondID"];

    // Save the new note...

    Response.Redirect("http://example.com/Page.aspx?BondID=" + bondID);

然后,当浏览器加载重定向时:

protected void Page_Load(object sender, EventArgs e)

    if (!IsPostBack)
    
        bindNotes(Request.QueryString["BondID"]); 
    

我们需要更改每个回发方法以使用此模式。某些方法可能需要在页面读取的重定向 URL 中添加额外的参数来配置显示。

【讨论】:

抱歉回复晚了,我周末休息了,没有注意到。我明白你的意思,它会完美地工作,但我想避免配置显示状态。根据他们决定单击哪个子菜单,我在某些 div 上做了很多可见 =真/假。我必须存储一个状态,然后显示/隐藏适当的 div。这可能意味着添加了数十种显示配置。我想完全避免这种情况,但如果我在实施显示配置系统时不那么固执,你的答案会很完美。感谢您的时间和精力。非常感谢。 @Athanasios - 我听到了 - 手动跟踪很多状态。不幸的是,当我们想要进行完整的服务器端渲染时,这是最强大的方法。它还创建用户可以轻松添加书签、共享和返回的 URL。当心服务器端状态跟踪会污染会话并创建难以发现的错误。或者,我们可以使用 javascript 和 AJAX 管理这个状态客户端。 ASP.NET 提供了some tools 可能会有所帮助。然而,这个问题表明我们投资于服务器端风格。 我将其标记为正确。您的回答是正确的,但它不一定适合我的项目,因为我解释说我特别不想重定向到该页面。因为我的用户会根据他们的工作来显示/隐藏不同的 div,而我不想实现某种状态保存功能来知道要显示/隐藏哪个 div 以及使用哪些参数运行哪些数据绑定函数。这太费时间了。【参考方案2】:

这非常简单,并且一旦提交表单,就会阻止在刷新时要求重新提交表单的弹出窗口。只需将这行 javascript 代码放在文件的页脚处,就能看到它的神奇之处。

<script>
if ( window.history.replaceState ) 
  window.history.replaceState( null, null, window.location.href );

</script>

【讨论】:

以上是关于防止表格重新提交的主要内容,如果未能解决你的问题,请参考以下文章

联系表格 7 防止重复字段值提交

如何在表单提交时防止表单在刷新时重新提交

防止在 django 中提交按钮后重新加载

在重新提交许多提交时如何防止许多 git 冲突?

如何防止“确认表单重新提交”对话框?

表单提交后如何防止页面重新加载 - JQuery