2017-10-06 235 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以及使用哪些參數運行哪些數據綁定功能。這太耗時了。 –