Ответ.Перенаправление с POST вместо Get?



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



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



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



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

696   13  

13 ответов:

для этого требуется понимание того, как работают перенаправления 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 к кнопке submit, которое сначала выполняет запрос AJAX на ваш сервер с данными, а затем позволяет отправить форму на сторонний сервер.
  2. создайте форму для публикации на вашем сервере. Когда форма будет отправлена, покажите пользователю страницу, на которой есть форма со всеми данными, которые вы хотите передать, все в скрытых входах. Просто показать сообщение типа " перенаправление...". Затем добавьте событие javascript на страницу, которая отправляет форму на сторонний сервер.

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

надеюсь, что это помогает!

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

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.

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

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

вы получаете дополнительное преимущество, что вам не нужно называть все ваши серверные элементы управления, чтобы сделать форму 3rd parties, вы можете сделать этот перевод при создании строки 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");

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

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

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

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

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

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

@Matt,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

это

способ выполнения HTTP-поездок туда и обратно без бессмысленных URL-адресов и JavaScript

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

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

Comments

    Ничего не найдено.