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

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

235 голосов
/ 05 сентября 2008

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

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

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

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

Ответы [ 14 ]

218 голосов
/ 06 сентября 2008

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

Другим вариантом является использование HTTP Status Code 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, при условии, что оно работает для всех ваших пользователей.

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

113 голосов
/ 10 мая 2010

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

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 с сервера, происходит событие onload , которое вызывает отправку и отправку всех данных в определенный postbackUrl.

31 голосов
/ 05 сентября 2008

Для этого используется 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, и это может работать, а может и не работать.

7 голосов
/ 29 августа 2012

Это должно сделать жизнь намного проще. Вы можете просто использовать метод 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
5 голосов
/ 13 мая 2010

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

4 голосов
/ 25 октября 2013

Думаю, что было бы интересно поделиться тем, что 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>
3 голосов
/ 05 сентября 2008

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

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

2 голосов
/ 09 сентября 2008

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

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

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

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

2 голосов
/ 07 сентября 2008

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

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

2 голосов
/ 06 сентября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...