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

233

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

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

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

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

  • 0
    В PHP вы можете отправлять POST-данные с помощью cURL. Есть ли что-то сопоставимое для .NET?
  • 0
    Я думаю, что это простой ответ, который вы искали. Я не мог поверить, насколько это гениально ... stackoverflow.com/a/6062248/110549
Показать ещё 1 комментарий
Теги:
https
response.redirect

13 ответов

201
Лучший ответ

Для этого требуется понимание того, как работают перенаправления HTTP. Когда вы используете Response.Redirect(), вы отправляете ответ (в браузер, который сделал запрос) с HTTP Status Code 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. Есть два варианта, которые я могу придумать с головы:

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

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

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

  • 2
    Как насчет этого метода? stackoverflow.com/a/6062248/110549
  • 1
    Пожалуйста, измените «добавить событие клика к кнопке отправки» на «добавить событие отправки в форму». Существует более одного метода для отправки формы, например, нажмите Enter, когда любой текст выделен, не говоря уже о программной отправке.
Показать ещё 1 комментарий
104

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

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.

  • 8
    +1 (я бы добавил тебя больше, если бы мог). Это ответ. Красноречивый и по существу. Вы даже включили весь код, чтобы сделать это, вместо того, чтобы говорить с головы до головы. Я использовал это в iframe на моей странице aspx, и он отрисовал все отлично - никаких переписывающих URL. Превосходная работа! СОВЕТ для тех, кто использует iframe: я указываю свой iframe на другую страницу aspx, которая затем выполняет этот код.
  • 1
    Это работает! Единственное плохое, что я вижу, это то, что если вы нажмете кнопку «Назад» браузера, он снова выполнит запрос, так что вы не сможете больше перейти на предыдущую страницу. Есть ли обходной путь для этого?
Показать ещё 6 комментариев
29

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

  • 0
    это то, что я собираюсь использовать, если я хочу дополнительную форму, чем форма asp.net. Мне нужно опубликовать некоторые данные на внешний URL-адрес, который предназначен для безопасного платежа 3d, затем мне нужно получить информацию, возвращаемую из запроса. Это способ сделать это? Спасибо
  • 0
    Если вам нужен пользователь, чтобы увидеть ответ, вы можете просто отправить обратно содержимое и любые соответствующие заголовки. Возможно, вам придется изменить некоторые аспекты результата в зависимости от использования относительных ресурсов, но это, безусловно, возможно.
Показать ещё 1 комментарий
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
5

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

3

Подумал, что может быть интересно поделиться тем, что герой делает это с ним 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

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

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

2

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

Да, HttpWebRequest, см. мое сообщение ниже.

2

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

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

  • 0
    Это работает, но важно отметить, что вы теряете все функции сервера ASP.NET.
2

@Matt,

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

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

1

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

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

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

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

1

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

0

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

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

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

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

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

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

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

Это способ < noscript/" > совершать обходы по HTTP без бессмысленных URL-адресов и JavaScript

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

Идея заключается в том, что вы вызываете 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;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню