2017-10-06 236 views
3

我一直在寻找这个答案几个星期,但许多其他问题/答案不适合我的项目或根本不工作。防止表单重新提交

让我解释一下我的项目。 这是一个简单的视图页面,它在页面加载时会将文本设置为整个页面的不同字段。在底部,我有一个带按钮的容器,它控制容器中显示哪个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”时,代码会触发来运行sqlcommand,然后通过调用“bindNotes(bondID)”来刷新ListView。如果用户点击F5或因任何原因点击刷新,他们将重新提交该表单并添加重复项。我已经阅读了很多重定向到同一页面的解决方案,这对我来说不起作用,因为div会再次隐藏,我更喜欢Notes div保持可见状态,以便用户可以在列表视图中看到他们的新音符。我也不希望有一个SQL检查来查找重复并防止插入,因为我可能希望用户插入重复项,如果他们选择在另一个时间,但用户将手动输入重复项。

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

还请在代码中没有批评,我要寻找的答案,而不是为什么它的坏的建立与联字符串添加SQL语句的讲座。我将最终纠正并优化这些故障。

如果您在其他地方找到了我的解决方案,请确保它适用于我的案例,因为我多次发现了同一个问题,但与我的场景无关。

在此先感谢。

+0

用'asp:updatePanel'封装你的'asp:listView'并将'trigers'设置为你的按钮。 –

回答

2

如您所知,ASP.NET Webforms将POST POST请求发送回服务器,并在同一请求中重新呈现Page。这就是我们点击“重新加载”时看到表单重新提交信息的原因。

为了避免这个问题,我们应该使用许多Web应用程序使用的后重定向模式。下面是一个简单的概述:

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

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

要将此应用于问题中的Webform,我们需要更改关于如何加载页面的一些假设。而不是运行的每个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添加其他参数以配置显示。

+0

对不起,迟到了,我周末休息了,没有注意到。我明白你的意思,它会完美的工作,但我想避免配置显示状态。根据他们决定点击哪个子菜单,我在某些div上做了很多visible = true/false。我将不得不存储一个状态,然后显示/隐藏适当的div。这可能意味着添加了数十种显示配置。我想完全避免这种情况,但是如果我不太执着于实现显示配置系统,那么您的答案会很好。感谢您的时间和精力。非常感谢。 –

+0

@Ahanasios - 我听到你 - 这是一个很多的状态来手动跟踪。不幸的是,当我们想要进行完整的服务器端渲染时,这是最稳健的方法。它还创建用户可以轻松收藏,共享和返回的网址。谨防服务器端状态跟踪可能会污染会话并创建难以发现的错误。另外,我们可以用JavaScript和AJAX管理这个状态客户端。 ASP.NET提供了[一些工具](https://msdn.microsoft.com/en-us/library/bb399001.aspx),可能有所帮助。但是,这个问题表明我们投入了服务器端的风格。 –

+1

我将其标记为正确。你的答案是正确的,但它不一定适合我的项目,因为我解释说我不想重定向到页面。由于我的用户会根据他们做什么显示/隐藏不同的div,我不想实现某种状态保存功能来知道显示/隐藏哪个div以及使用哪些参数运行哪些数据绑定功能。这太耗时了。 –