Response.Redirect与POST而不是Get?

问题描述 投票:235回答:14

我们需要提交表单并保存一些数据,然后将用户重定向到异地页面,但在重定向时,我们需要使用POST“提交”表单,而不是GET。

我希望有一个简单的方法来实现这一目标,但我开始认为没有。我想我现在必须创建一个简单的其他页面,只需要我想要的表单,重定向到它,填充表单变量,然后对只调用document.forms [0] .submit的脚本执行body.onload调用( );

任何人都可以告诉我是否有其他选择?我们可能需要稍后在项目中调整它,它可能会变得有点复杂,所以如果有一个简单的我们可以做到这一切所有非其他页面依赖,这将是太棒了。

无论如何,感谢任何和所有的回应。

asp.net https response.redirect
14个回答
220
投票

这样做需要了解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");
}

不幸的是,这并不总是有效。 Different browsers implement this differently,因为它不是常见的状态代码。

唉,与Opera和FireFox开发人员不同,IE开发人员从未阅读过规范,即使最新的,最安全的IE7也会将POST请求从域A重定向到域B,而不会发出任何警告或确认对话框! Safari也以有趣的方式运行,虽然它没有引发确认对话框并执行重定向,但它会丢弃POST数据,有效地将307重定向更改为更常见的302。

所以,据我所知,实现这样的事情的唯一方法是使用Javascript。我可以想到两个选项:

  1. 创建表单并将其action属性指向第三方服务器。然后,将click事件添加到提交按钮,该按钮首先使用数据向服务器执行AJAX请求,然后允许将表单提交给第三方服务器。
  2. 创建表单以发布到您的服务器。提交表单时,向用户显示一个页面,其中包含您要传递的所有数据的表单,所有这些都在隐藏的输入中。只需显示“重定向...”等消息即可。然后,将javascript事件添加到将表单提交给第三方服务器的页面。

在这两个中,我会选择第二个,原因有两个。首先,它比第一个更可靠,因为它不需要Javascript工作;对于那些没有启用它的人,你总是可以看到隐藏表单的提交按钮,并指示他们按下它需要超过5秒。其次,您可以决定将哪些数据传输到第三方服务器;如果你只使用处理表单,你将传递所有的帖子数据,这并不总是你想要的。同样适用于307解决方案,假设它适用于所有用户。

希望这可以帮助!


2
投票

在PHP中,您可以使用cURL发送POST数据。是否有类似于.NET的东西?

是的,HttpWebRequest,请参阅下面的帖子。


2
投票

绝不应该使用GET(和HEAD)方法来做任何有副作用的方法。副作用可能是更新Web应用程序的状态,或者可能是从您的信用卡收费。如果某个动作有副作用,则应使用另一种方法(POST)。

因此,用户(或他们的浏览器)不应对GET完成的任务负责。如果由于GET而发生一些有害或昂贵的副作用,那将是Web应用程序的错误,而不是用户的错误。根据规范,用户代理不得自动遵循重定向,除非它是对GET或HEAD请求的响应。

当然,很多GET请求确实有一些副作用,即使它只是附加到日志文件。重要的是应用程序而不是用户应对这些影响负责。

HTTP规范的相关部分是9.1.1 and 9.1.210.3


1
投票

我建议构建一个HttpWebRequest以编程方式执行POST,然后在读取响应后重定向(如果适用)。


1
投票

基于Pavlo Neyman's method的可复制粘贴代码

RedirectPost(string url,T bodyPayload)和GetPostData()适用于那些只想在源页面中转储一些强类型数据并在目标页面中获取它的人。数据必须可由NewtonSoft Json.NET序列化,您当然需要引用该库。

只需复制粘贴到您的页面或更好的基页类,然后在您的应用程序中的任何位置使用它。

无论出于何种原因,我仍然向所有仍然需要在2019年使用Web Forms的人致敬。

        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;
        }

0
投票

通常,您需要的只是在这两个请求之间携带一些状态。实际上有一种非常时髦的方式,它不依赖于JavaScript(想想<noscript />)。

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

有了那个cookie,你可以在以下请求中/redirect.html检索name = value info,你可以在这个名称/值对字符串中存储任何类型的信息,最多可以说4K数据(典型的cookie限制)。当然你应该避免这种情况,而是存储状态代码和标志位。

收到此请求后,您将回复该状态代码的删除请求。

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

我的HTTP有点生疏我已经通过RFC2109和RFC2965来确定这是多么可靠,最好是我希望cookie只进行一次往返,但这似乎不可能,第三方cookie如果您要重新定位到另一个域,可能会出现问题。这仍然是可能的,但不像你在自己的领域内做的那样轻松。

这里的问题是并发性,如果高级用户使用多个选项卡并设法交错属于同一会话的几个请求(这是非常不可能的,但并非不可能),这可能会导致应用程序出现不一致。

这是在没有无意义的URL和JavaScript的情况下进行HTTP往返的<noscript />方式

我提供此代码作为概念证明:如果此代码在您不熟悉的上下文中运行,我认为您可以找出哪些部分是什么。

这个想法是你在重定向时调用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;
}

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之后,事件onload发生触发表单提交并将所有数据发布到定义的postbackUrl。


31
投票

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
投票

这应该让生活更轻松。您可以轻松地在Web应用程序中使用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中的新功能是ASP按钮的“PostBackUrl”属性。您可以将其设置为要直接发布到的页面的地址,当单击该按钮时,而不是像往常一样回发到同一页面,而是发布到您指定的页面。便利。确保UseSubmitBehavior也设置为TRUE。


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>

3
投票

可以在asp按钮上设置PostbackUrl以发布到其他页面。

如果你需要在后面的代码中执行它,请尝试Server.Transfer。


2
投票

@马特,

您仍然可以使用HttpWebRequest,然后将您收到的响应指向实际的输出流响应,这会将响应提供给用户。唯一的问题是任何相对网址都会被破坏。

不过,这可能有用。


2
投票

这是我要做的:

将数据放在标准格式(没有runat =“server”属性)中,并将表单的操作设置为发布到目标场外页面。在提交之前,我会使用XmlHttpRequest将数据提交给我的服务器并分析响应。如果响应意味着您应该继续进行异地POSTing,那么我(JavaScript)将继续发布该帖子,否则我将重定向到我的网站上的页面

© www.soinside.com 2019 - 2024. All rights reserved.