响应。用POST而不是Get重定向?

我们需要进行表单提交并保存一些数据,然后将用户重定向到站点外的页面,但是在重定向过程中,我们需要使用POST而不是GET“提交”表单。

我希望有一个简单的方法来完成这一点,但我开始觉得没有。我想我现在必须创建一个简单的另一个页面,只有我想要的表单,重定向到它,填充表单变量,然后做一个主体。Onload调用脚本,只调用document.forms[0].submit();

谁能告诉我还有别的选择吗?我们可能需要在项目后期进行调整,它可能会变得有点复杂,所以如果有一个简单的方法,我们可以做所有不依赖于其他页面的事情,那就太棒了。

无论如何,感谢所有的回复。

285757 次浏览

PostbackUrl可以在asp按钮上设置,以便发布到不同的页面。

如果你需要在代码背后,尝试Server.Transfer。

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

HttpWebRequest用于此。

在回发时,创建一个HttpWebRequest到你的第三方并发布表单数据,然后一旦完成,你就可以响应了。转向你想去的地方。

你得到了额外的好处,你不必为所有的服务器控件命名来创建第三方表单,你可以在构建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。转移,可能有用,也可能没用。

@Matt,

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

不过,这可能奏效。

我会这么做:

将数据放在标准表单中(没有runat="server"属性),并设置表单的动作以发布到目标场外页面。 在提交之前,我会将数据提交给我的服务器使用XmlHttpRequest并分析响应。如果响应意味着你应该继续与场外张贴然后我(JavaScript)将继续与张贴,否则我会重定向到我的网站上的一个页面

做到这一点需要理解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属性指向第三方服务器。然后,向提交按钮添加一个单击事件,该事件首先使用数据向服务器执行AJAX请求,然后允许将表单提交给第三方服务器。
  2. 创建要发布到服务器的表单。提交表单时,向用户显示一个页面,该页面中有一个表单,其中包含您想要传递的所有数据,所有数据都在隐藏输入中。只要显示一条像“重定向…”这样的信息。然后,向页面添加一个javascript事件,将表单提交给第三方服务器。

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

希望这能有所帮助!

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

是的,HttpWebRequest,请看我下面的帖子。

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

因此,用户(或他们的浏览器)不应该对GET所做的事情负责。如果由于GET而产生了一些有害或昂贵的副作用,那将是web应用程序的错,而不是用户的错。根据规范,用户代理不得自动跟随重定向,除非它是对GET或HEAD请求的响应。

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

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

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

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

有了这个cookie,你可以在下面的请求/redirect.html中检索name=value信息,你可以在这个名称/值对字符串中存储任何类型的信息,最多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;
}

你可以使用这个方法:

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。

ASP中的一些新东西。Net 3.5是ASP按钮的“PostBackUrl”属性。您可以将其设置为您想要直接发布到的页面的地址,当单击该按钮时,它不会像正常情况下那样发布回同一页面,而是会发布到您所指示的页面。方便。确保UseSubmitBehavior也被设置为TRUE。

这将使生活更容易。 您可以简单地在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

认为这可能是有趣的分享,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>

基于Pavlo Neyman的方法的可复制粘贴代码

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