Предотвратить повторную отправку формы в ASP.Net (без перенаправления на себя)

У меня есть главная страница с элементом Form (<form runat="server">), страница содержимого с элементом Button (<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Sync" />) и страница кода программной части, содержащая функцию Button1_Click.

Пользователь заходит на страницу и нажимает кнопку. Код Code Behind выполняется (на сервере), в котором он обновляет некоторые таблицы в базе данных. Последнее, что делает код программной части, — это устанавливает InnerHTML элемента Span на странице содержимого с сообщением об успехе или неудаче.

Это все прекрасно работает. Проблема в том, что если пользователь обновляет страницу, форма отправляется повторно, и браузер спрашивает, действительно ли это то, чего хочет пользователь. Если пользователь отвечает утвердительно, код программной части выполняется повторно, и база данных снова обновляется. Если пользователь отвечает отрицательно, то ничего не происходит.

Повторное выполнение кода Code Behind не представляет большой проблемы. Ничего не повредит. Но это не совсем то поведение, которое мне нужно.

Я знаю, что могу сделать перенаправление обратно на страницу с помощью Response.Redirect(), но тогда пользователь никогда не увидит мое сообщение об успехе или неудаче. Я должен упомянуть, что сообщение действительно больше, чем просто «Успех» или «Ошибка», иначе я полагаю, что мог бы добавить что-то в QueryString при перенаправлении.

Есть ли способ сбросить элемент формы из кода программной части, чтобы, если пользователь обновляет страницу, форма не отправлялась повторно?

Главная страница...

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="IntuitSync.SiteMaster" %>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:ipp="">
    <head runat="server">
    </head>
    <body>
        <form runat="server">
            <div runat="server" id="mainContetntDiv">
                <asp:ContentPlaceHolder ID="MainContent" runat="server" />
            </div>
        </form>
    </body>
</html>

Содержимое...

<%@ Page Language="C#" AutoEventWireup="true" MasterPageFile="~/Site.master" CodeBehind="SyncToCloud.aspx.cs" Inherits="IntuitSync.SyncToCloud" %>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Sync" />
    <span runat="server" id="SyncStatus"></span>
</asp:Content>

Код позади...

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Globalization;
using System.Web;

namespace IntuitSync
{
    public partial class SyncToCloud : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            //
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            /*
                Do a bunch of stuff and put the results in syncResults which is a List.
            */
            SyncStatus.InnerHtml = string.Join("", syncResults.ToArray()); // I'd rather do this...
            //Response.Redirect(Request.Url.PathAndQuery, true);  // ...and not do this.
        }
    }
}

Любая помощь приветствуется.


person user2008632    schedule 07.02.2013    source источник


Ответы (2)


Проблема в том, что вы выполняете форму POST, которая, если пользователь впоследствии обновляет страницу, делает то, что должна делать: повторно отправляет POST. Нет никакого способа обойти это. Таким образом, вам доступны следующие варианты:

  1. Перенаправление на страницу подтверждения, передача результатов операции на страницу подтверждения каким-либо образом (обычно перезагрузка из какого-либо репозитория данных, если это невозможно для строки запроса/файла cookie/и т. д.).
  2. Перенаправьте на ту же страницу, но передайте сообщение об успехе через параметр строки запроса (или сделайте глупость и сохраните его в session/ViewState/what-have-you-silliness).
  3. Вместо публикации выполните получение формы, поскольку кажется, что вы не публикуете никаких значений, а только инициируете какую-то обработку на стороне сервера. Для этого просто измените свою форму с поста на получение или просто сделайте ссылку. Опасность здесь в том, что вы не должны выполнять какие-либо преобразующие/разрушающие операции в любой операции получения.

ПРИМЕЧАНИЕ: убедитесь, что вы дезинфицируете любой пользовательский ввод (всегда относитесь к пользовательскому вводу как к злу).

Вы, вероятно, были бы счастливы с последним вариантом, но все зависит от того, что на самом деле влечет за собой "сделать кучу вещей и поместить результаты в syncResults...".

ОБНОВЛЕНИЕ (годы спустя)

Хотя это должно быть очевидно, для некоторых пользователей это может быть недостаточно очевидно: НИКОГДА не следует подвергать себя атакам XSS, напрямую отображая недезинфицированное «сообщение об успехе через параметр строки запроса». В предложениях предполагалось, что это очевидно, но в ретроспективе это должно было быть ясно в этом отношении.

person Ted    schedule 07.02.2013
comment
Спасибо, Тед. Да, вы правы, на самом деле я не публикую никаких значений из формы. Если бы это зависело от меня, я бы удалил элемент формы с главной страницы, но я понимаю, что кнопка не будет вызывать код программной части без него. Я больше классический парень ASP и новичок в ASP.NET. Я только что изменил его с Post на Get, и теперь я получаю какие-то данные _VIEWSTATE в QueryString. Несомненно, ASP.Net (из-за отсутствия лучшего термина) должен сделать это, чтобы иметь возможность хранить все части вместе. Я могу просто перейти к варианту 1 и перестать бороться с ним. - person user2008632; 08.02.2013
comment
Если вам не нужен ViewState на странице, отключите его. В вашем случае вы определенно не используете эту страницу, поэтому отключите ее на уровне страницы. - person Ted; 08.02.2013
comment
И зачем беспокоиться о том, что у него должна быть кнопка? Просто укажите ссылку на URL-адрес /a и переключите свою логику с button_click на page_load... - person Ted; 08.02.2013
comment
Я не знал, что могу отключить ViewState. Я посмотрю на это. Почему кнопка? Я хотел, чтобы пользователь мог просмотреть данные перед их обработкой. На странице показаны данные, которые находятся в базе данных, но должны быть синхронизированы с пользовательской настольной версией QuickBooks. Например, в базе данных может быть 10 новых записей о клиентах, которые необходимо синхронизировать с QuickBooks. syncResults покажет, сколько из 10 записей было успешно синхронизировано (все 10, 9 из 10 и т. д.). - person user2008632; 08.02.2013
comment
Лично у меня была бы страница обзора (просмотр незавершенных записей) и страница подтверждения (либо процесс там, который склонен к краже дополнительных записей и требует неиндемпотентного процесса - пометки как импортированного - или еще лучше, процесс на странице обзора, сохранить в хранилище данных и перезагрузить результаты на странице просмотра Я даже не буду спрашивать, как вы планируете синхронизировать веб-приложение с вашим настольным QB. - person Ted; 08.02.2013
comment
Или, что еще лучше, просто сделайте ajax-вызов службы. Легкий и, безусловно, лучший. - person Ted; 08.02.2013
comment
Да, обычно я делаю что-то подобное с ajax (js на клиенте и классический asp на сервере). Это часть QB, которая усложняет это. Я знаю только, как это сделать с помощью .Net и C#. Ни один из них я не использовал раньше, что означает, что я действительно не могу ничего сделать, кроме как использовать примеры, которые я получил от Intuit. Но мне нравится идея превратить его в веб-сервис. Пища для размышлений. Спасибо еще раз. - person user2008632; 08.02.2013
comment
Не совсем понимаю, как вы планируете реализовать веб-сервер, взаимодействующий с настольной установкой QB... независимо от того, используете ли вы QB API с .NET. - person Ted; 09.02.2013

я бы предложил перейти на маршрут JASON

на SyncToCloud.aspx.cs

[System.Web.Services.WebMethod]
    public static string updateSyncStatus()
    {
        /*
                Do a bunch of stuff and put the results in syncResults which is a List.
            */
            SyncStatus.InnerHtml = string.Join("", syncResults.ToArray()); // I'd rather do this...
            //Response.Redirect(Request.Url.PathAndQuery, true);  // ...and not do this.
    }

на SyncToCloud.aspx

function SyncStatus(){
                $.ajax({
                            type: "POST",
                            async:true,
                            url: "syncToCloud.aspx/updateSyncStatus",
                            data: <if have anydata>,
                            contentType: "application/json; charset=utf-8",
                            dataType: "json",
                            success: function (msg) {
                                if (msg.d == "Success") {                                    
                                    //Set message on some lable or do something
                                } else {
                                    //Proceed to show message on wrong password
                                }
                            } 
                        }); 
            }

Разметка на кнопке

<asp:Button ID="Button1" runat="server" OnClientClick="SyncStatus(); return false;" Text="Sync" />
person Kamran Ul Haq    schedule 22.07.2015