Response.Redirect с POST вместо Get?

asp.net https response.redirect

224860 просмотра

14 ответа

У нас есть требование принять форму и сохранить некоторые данные, а затем перенаправить пользователя на страницу вне сайта, но при перенаправлении нам нужно «отправить» форму с помощью POST, а не GET.

Я надеялся, что есть простой способ сделать это, но я начинаю думать, что нет. Я думаю, что теперь я должен создать простую другую страницу с нужной мне формой, перенаправить на нее, заполнить переменные формы, а затем выполнить вызов body.onload для сценария, который просто вызывает document.forms [0] .submit ( );

Может кто-нибудь сказать мне, если есть альтернатива? Возможно, нам понадобится настроить это позже в проекте, и это может стать чем-то сложным, поэтому, если бы это было легко, мы могли бы сделать все это без постраничной зависимости, что было бы фантастически.

В любом случае, спасибо за любые ответы.

Автор: Matt Dawdy Источник Размещён: 17.05.2019 02:55

Ответы (14)


216 плюса

Решение

Для этого необходимо понять, как работает перенаправление HTTP. При использовании Response.Redirect()вы отправляете ответ (браузеру, который сделал запрос) с кодом состояния HTTP 302 , который сообщает браузеру, куда идти дальше. По определению браузер сделает это с помощью GETзапроса, даже если исходный запрос был POST.

Другой вариант - использовать код состояния HTTP 307 , который указывает, что браузер должен сделать запрос на перенаправление таким же образом, как и исходный запрос, но для предупреждения пользователя с предупреждением безопасности. Для этого вы должны написать что-то вроде этого:

public void PageLoad(object sender, EventArgs e)
{
    // Process the post on your side   

    Response.Status = "307 Temporary Redirect";
    Response.AddHeader("Location", "http://example.com/page/to/post.to");
}

К сожалению, это не всегда работает. Различные браузеры реализуют это по-разному , поскольку это не общий код состояния.

Увы, в отличие от разработчиков Opera и FireFox, разработчики IE никогда не читали спецификации, и даже самый последний, самый безопасный IE7 перенаправит POST-запрос из домена A в домен B без каких-либо предупреждений или диалогов подтверждения! Safari также действует интересным образом, хотя он не вызывает диалоговое окно подтверждения и выполняет перенаправление, он выбрасывает данные POST, эффективно изменяя перенаправление 307 в более распространенное 302.

Итак, насколько я знаю, единственный способ реализовать что-то подобное - это использовать Javascript. Есть два варианта, которые я могу придумать вне головы:

  1. Создайте форму и actionукажите ее атрибут на стороннем сервере. Затем добавьте событие click к кнопке отправки, которая сначала выполняет запрос AJAX на ваш сервер с данными, а затем позволяет отправлять форму на сторонний сервер.
  2. Создайте форму для публикации на вашем сервере. Когда форма отправлена, покажите пользователю страницу, на которой есть форма со всеми данными, которые вы хотите передать, все в скрытом виде. Просто покажите сообщение типа «Перенаправление ...». Затем добавьте событие javascript на страницу, которая отправляет форму на сторонний сервер.

Из двух я бы выбрал второе по двум причинам. Во-первых, он более надежен, чем первый, потому что для его работы не требуется Javascript; для тех, у кого она не включена, вы всегда можете сделать видимой кнопку отправки для скрытой формы и дать им команду нажать ее, если это займет более 5 секунд. Во-вторых, вы можете решить, какие данные будут передаваться на сторонний сервер; если вы просто обрабатываете форму по мере ее прохождения, вы будете передавать все почтовые данные, что не всегда то, что вы хотите. То же самое для решения 307, при условии, что оно работает для всех ваших пользователей.

Надеюсь это поможет!

Автор: tghw Размещён: 06.09.2008 06:54

114 плюса

Вы можете использовать этот подход:

Response.Clear();

StringBuilder sb = new StringBuilder();
sb.Append("<html>");
sb.AppendFormat(@"<body onload='document.forms[""form""].submit()'>");
sb.AppendFormat("<form name='form' action='{0}' method='post'>",postbackUrl);
sb.AppendFormat("<input type='hidden' name='id' value='{0}'>", id);
// Other params go here
sb.Append("</form>");
sb.Append("</body>");
sb.Append("</html>");

Response.Write(sb.ToString());

Response.End();

В результате сразу после того, как клиент получит все html с сервера, происходит загрузка по событию, которая вызывает отправку и отправку всех данных в определенный postbackUrl.

Автор: Pavlo Neyman Размещён: 10.05.2010 01:16

30 плюса

HttpWebRequest используется для этого.

При обратной передаче создайте запрос HttpWebRequest для третьей стороны и опубликуйте данные формы, а затем, как только это будет сделано, вы можете Response.Redirect куда угодно.

Вы получаете дополнительное преимущество, заключающееся в том, что вам не нужно называть все свои элементы управления сервером для создания сторонней формы, вы можете выполнить этот перевод при построении строки POST.

string url = "3rd Party Url";

StringBuilder postData = new StringBuilder();

postData.Append("first_name=" + HttpUtility.UrlEncode(txtFirstName.Text) + "&");
postData.Append("last_name=" + HttpUtility.UrlEncode(txtLastName.Text));

//ETC for all Form Elements

// Now to Send Data.
StreamWriter writer = null;

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";                        
request.ContentLength = postData.ToString().Length;
try
{
    writer = new StreamWriter(request.GetRequestStream());
    writer.Write(postData.ToString());
}
finally
{
    if (writer != null)
        writer.Close();
}

Response.Redirect("NewPage");

Однако, если вам нужно, чтобы пользователь увидел страницу ответа из этой формы, вы можете использовать только Server.Transfer, и это может работать, а может и не работать.

Автор: FlySwat Размещён: 05.09.2008 07:33

6 плюса

Это должно сделать жизнь намного проще. Вы можете просто использовать метод Response.RedirectWithData (...) в своем веб-приложении.

Imports System.Web
Imports System.Runtime.CompilerServices

Module WebExtensions

    <Extension()> _
    Public Sub RedirectWithData(ByRef aThis As HttpResponse, ByVal aDestination As String, _
                                ByVal aData As NameValueCollection)
        aThis.Clear()
        Dim sb As StringBuilder = New StringBuilder()

        sb.Append("<html>")
        sb.AppendFormat("<body onload='document.forms[""form""].submit()'>")
        sb.AppendFormat("<form name='form' action='{0}' method='post'>", aDestination)

        For Each key As String In aData
            sb.AppendFormat("<input type='hidden' name='{0}' value='{1}' />", key, aData(key))
        Next

        sb.Append("</form>")
        sb.Append("</body>")
        sb.Append("</html>")

        aThis.Write(sb.ToString())

        aThis.End()
    End Sub

End Module
Автор: ZooZ Размещён: 29.08.2012 09:44

5 плюса

Что-то новое в ASP.Net 3.5 - это свойство «PostBackUrl» для кнопок ASP. Вы можете установить для него адрес страницы, на которую вы хотите опубликовать сообщение, и при нажатии этой кнопки вместо публикации на той же странице, как обычно, он вместо этого отправляет на указанную вами страницу. Handy. Убедитесь, что UseSubmitBehavior также имеет значение TRUE.

Автор: Mike K Размещён: 13.05.2010 07:15

4 плюса

Я подумал, что было бы интересно поделиться тем, что heroku делает это с SSO для поставщиков дополнений

Пример того, как это работает, можно увидеть в исходном коде инструмента "kensa":

https://github.com/heroku/kensa/blob/d4a56d50dcbebc2d26a4950081acda988937ee10/lib/heroku/kensa/post_proxy.rb

И это можно увидеть на практике, если вы включите JavaScript. Пример страницы источника:

<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Heroku Add-ons SSO</title>
  </head>

  <body>
    <form method="POST" action="https://XXXXXXXX/sso/login">

        <input type="hidden" name="email" value="XXXXXXXX" />

        <input type="hidden" name="app" value="XXXXXXXXXX" />

        <input type="hidden" name="id" value="XXXXXXXX" />

        <input type="hidden" name="timestamp" value="1382728968" />

        <input type="hidden" name="token" value="XXXXXXX" />

        <input type="hidden" name="nav-data" value="XXXXXXXXX" />

    </form>

    <script type="text/javascript">
      document.forms[0].submit();
    </script>
  </body>
</html>
Автор: jacob Размещён: 25.10.2013 07:34

3 плюса

PostbackUrl может быть установлен на кнопку asp для публикации на другой странице.

если вам нужно сделать это в codebehind, попробуйте Server.Transfer.

Автор: Jimmy Размещён: 05.09.2008 07:11

2 плюса

@ Matt,

Вы все еще можете использовать HttpWebRequest, а затем перенаправить полученный ответ на фактический ответ outputtream, который будет возвращать ответ пользователю. Единственная проблема заключается в том, что любые относительные URL будут нарушены.

Тем не менее, это может сработать.

Автор: FlySwat Размещён: 05.09.2008 09:15

2 плюса

Вот что я бы сделал:

Поместите данные в стандартную форму (без атрибута runat = "server") и установите действие формы для публикации на целевой странице за пределами сайта. Перед отправкой я отправляю данные на свой сервер с помощью XmlHttpRequest и анализирую ответ. Если ответ означает, что вы должны продолжить POSTing вне сайта, тогда я (JavaScript) продолжу публикацию, в противном случае я перенаправлю на страницу на моем сайте.

Автор: Andrei Rînea Размещён: 06.09.2008 05:06

2 плюса

В PHP вы можете отправлять POST-данные с помощью cURL. Есть ли что-то сопоставимое для .NET?

Да, HttpWebRequest, см. Мой пост ниже.

Автор: FlySwat Размещён: 06.09.2008 09:02

1 плюс

Я предлагаю создать HttpWebRequest для программного выполнения вашего POST, а затем перенаправить после прочтения Ответа, если это применимо.

Автор: Ben Griswold Размещён: 05.09.2008 07:20

1 плюс

Метод GET (и HEAD) никогда не должен использоваться для выполнения каких-либо побочных эффектов. Побочным эффектом может быть обновление состояния веб-приложения или списание средств с вашей кредитной карты. Если у действия есть побочные эффекты, следует использовать другой метод (POST).

Таким образом, пользователь (или его браузер) не должен нести ответственность за то, что сделал GET. Если какой-либо вредный или дорогой побочный эффект произошел в результате GET, то это будет вина веб-приложения, а не пользователя. Согласно спецификации, пользовательский агент не должен автоматически выполнять перенаправление, если он не является ответом на запрос GET или HEAD.

Конечно, многие запросы GET имеют некоторые побочные эффекты, даже если они просто добавляются в файл журнала. Важно то, что приложение, а не пользователь, должно нести ответственность за эти эффекты.

Соответствующими разделами спецификации HTTP являются 9.1.1 и 9.1.2 и 10.3 .

Автор: erickson Размещён: 08.09.2008 10:02

0 плюса

Как правило, все, что вам когда-либо понадобится, - это переносить некоторое состояние между этими двумя запросами. На самом деле есть действительно забавный способ сделать это, который не зависит от JavaScript (подумайте

Set-Cookie: name=value; Max-Age=120; Path=/redirect.html

Имея этот файл cookie, вы можете в следующем запросе к /redirect.html получить информацию об имени = значении, вы можете сохранить любую информацию в этой строке пары имя / значение, вплоть до 4 КБ данных (типичное ограничение для файлов cookie). Конечно, вы должны избегать этого и хранить вместо этого коды состояния и биты флага.

Получив этот запрос, вы в ответ отвечаете запросом на удаление этого кода состояния.

Set-Cookie: name=value; Max-Age=0; Path=/redirect.html

Мой HTTP немного ржавый. Я прошёл через RFC2109 и RFC2965, чтобы понять, насколько это действительно надежно, предпочтительно, я бы хотел, чтобы куки -файлы отправлялись в оба конца ровно один раз, но это не представляется возможным, в том числе и сторонние куки-файлы. может быть проблемой для вас, если вы переезжаете в другой домен. Это все еще возможно, но не так безболезненно, как когда вы делаете что-то внутри своего домена.

Проблема здесь заключается в параллелизме, если опытный пользователь использует несколько вкладок и ему удается чередовать пару запросов, принадлежащих одному и тому же сеансу (это очень маловероятно, но не невозможно), это может привести к несоответствиям в вашем приложении.

Это способ

Я предоставляю этот код в качестве понятия концепции: если этот код выполняется в контексте, с которым вы не знакомы, я думаю, вы сможете решить, что это за часть.

Идея заключается в том, что при перенаправлении вы вызываете Relocate с некоторым состоянием, а URL-адрес, по которому вы переместились, вызывает GetState для получения данных (если они есть).

const string StateCookieName = "state";

static int StateCookieID;

protected void Relocate(string url, object state)
{
    var key = "__" + StateCookieName + Interlocked
        .Add(ref StateCookieID, 1).ToInvariantString();

    var absoluteExpiration = DateTime.Now
        .Add(new TimeSpan(120 * TimeSpan.TicksPerSecond));

    Context.Cache.Insert(key, state, null, absoluteExpiration,
        Cache.NoSlidingExpiration);

    var path = Context.Response.ApplyAppPathModifier(url);

    Context.Response.Cookies
        .Add(new HttpCookie(StateCookieName, key)
        {
            Path = path,
            Expires = absoluteExpiration
        });

    Context.Response.Redirect(path, false);
}

protected TData GetState<TData>()
    where TData : class
{
    var cookie = Context.Request.Cookies[StateCookieName];
    if (cookie != null)
    {
        var key = cookie.Value;
        if (key.IsNonEmpty())
        {
            var obj = Context.Cache.Remove(key);

            Context.Response.Cookies
                .Add(new HttpCookie(StateCookieName)
                { 
                    Path = cookie.Path, 
                    Expires = new DateTime(1970, 1, 1) 
                });

            return obj as TData;
        }
    }
    return null;
}
Автор: John Leidegren Размещён: 30.08.2009 11:50

0 плюса

Копируемый код, основанный на методе Павла Неймана

RedirectPost (string url, T bodyPayload) и GetPostData () предназначены для тех, кто просто хочет выгрузить некоторые строго типизированные данные на исходной странице и извлечь их на целевой странице. Данные должны быть сериализуемы NewtonSoft Json.NET, и вам, конечно же, нужно обращаться к библиотеке.

Просто скопируйте и вставьте на свою страницу (страницы) или, что еще лучше, базовый класс для своих страниц и используйте его в любом месте своего приложения.

Я сердечно приветствую всех вас, кто по каким-либо причинам все еще должен использовать веб-формы в 2019 году.

        protected void RedirectPost(string url, IEnumerable<KeyValuePair<string,string>> fields)
        {
            Response.Clear();

            const string template =
@"<html>
<body onload='document.forms[""form""].submit()'>
<form name='form' action='{0}' method='post'>
{1}
</form>
</body>
</html>";

            var fieldsSection = string.Join(
                    Environment.NewLine,
                    fields.Select(x => $"<input type='hidden' name='{HttpUtility.UrlEncode(x.Key)}' value='{HttpUtility.UrlEncode(x.Value)}'>")
                );

            var html = string.Format(template, HttpUtility.UrlEncode(url), fieldsSection);

            Response.Write(html);

            Response.End();
        }

        private const string JsonDataFieldName = "_jsonData";

        protected void RedirectPost<T>(string url, T bodyPayload)
        {
            var json = JsonConvert.SerializeObject(bodyPayload, Formatting.Indented);
            //explicit type declaration to prevent recursion
            IEnumerable<KeyValuePair<string, string>> postFields = new List<KeyValuePair<string, string>>()
                {new KeyValuePair<string, string>(JsonDataFieldName, json)};

            RedirectPost(url, postFields);

        }

        protected T GetPostData<T>() where T: class 
        {
            var urlEncodedFieldData = Request.Params[JsonDataFieldName];
            if (string.IsNullOrEmpty(urlEncodedFieldData))
            {
                return null;// default(T);
            }

            var fieldData = HttpUtility.UrlDecode(urlEncodedFieldData);

            var result = JsonConvert.DeserializeObject<T>(fieldData);
            return result;
        }
Автор: Zar Shardan Размещён: 28.02.2019 03:56
Вопросы из категории :
32x32