ASP.NET 中的重定向和 POST






4.78/5 (83投票s)
本文介绍了一种简单的解决方案,用于将数据 POST 并重定向到外部 URL。
引言
最近,我遇到一种情况,我有一个应用程序需要与第三方支付应用程序通信,在某个时候,我需要使用 POST 而不是 GET 方法将信息发送到该第三方应用程序。
背景
好吧,一开始听起来很简单,但事实并非如此。我对此进行了大量研究,并进行了密集的 Google 搜索,但一直没有找到令我满意的解决方案。让我们先看看提交数据(POST 或 GET)到目标 URL 的选项:
- Response.Redirect:此方法被广泛使用且很常见,但在 ASP.NET 中它只使用 GET 方法,无法通过它进行 POST。
- Server.Transfer:此方法执行 POST 而不是 GET,但是……不幸的是,它只在源应用程序和目标应用程序相同时才有效;因此,如果您向第三方应用程序发送请求,这将不起作用……太糟糕了 :S
- 使用 HttpWebRequest:这是另一种从头开始创建整个请求的方法,您指定内容、内容类型,将数据连接成“&”的形式,然后转换为字节,并通过调用
GetResponse()
方法提交请求……它执行 POST 吗?是的,它执行了!!!它会重定向到目标 URL 吗?不,它没有!!!即便如此,您仍然会在浏览器中为用户呈现响应,用户将完美地看到目标页面,但是在此页面上的任何操作都将以泪告终,因为您仍然在引用请求的同一个源……也就是您的应用程序……因此,该页面上的任何回发都将显示 404……页面未找到。(为什么?还记得默认的回发行为始终是针对页面本身的……所以 ;)。) - 使用 WebClient:此方法是我列出选项中最喜欢的……因为它很简单……创建一个
WebClient
对象,指定目标 URL,调用UploadValues
方法,该方法接受一个名称-值集合(其中包含您的所有 POST 数据),呈现响应,一切都完美地 POST 到目标 URL;但与前一种选择一样,您仍然处于同一位置,即您的应用程序……因此,该页面上的任何回发都将显示 404……页面未找到。 - 带有 POST 表单的 HTML 提交按钮:好吧,这是将 POST 数据发送到任何 URL 的标准方法,无论是您自己的应用程序还是第三方应用程序,但是这会限制您必须有一个提交按钮,例如
<input type="submit"/>
,当然,它不会为您执行任何服务器端事件!!!而且在某种程度上,您最终必须在某个时候按下该按钮……别无他法……太糟糕了 :S
注意:如果您有其他方法……请保留…… ;) 我已经受够了我看到的东西了!
现在,让我们想象一下,我有一个 Response.Redirect
方法,我向它提供一个数据集合,并告诉它执行 POST 方法而不是 GET 方法……那不是很棒吗?我认为那会很棒……真的只是一个小愿望!:)
现在,这才是重点,有一个方法,我向其提供一个要提交到目标 URL 的数据集合,然后所有数据都以重定向的方式 POST 过去。
使用代码
我在此文章中附带了一个文件,其中包含一个名为 HttpHelper
的类。该类只有两个方法:一个私有方法 PreparePOSTForm
和一个公共方法 RedirectAndPOST
。当然,您将只使用公共方法;私有方法只是用于封装公共方法所需的功能……没关系……您只需要调用公共方法,然后就完成了!
下面显示了该方法的使用方式
NameValueCollection data = new NameValueCollection();
data.Add("v1", "val1");
data.Add("v2", "val2");
HttpHelper.RedirectAndPOST(this.Page, "http://DestUrl/Default.aspx", data);
v1
和 v2
将会以重定向的方式 POST 到目标 URL;重定向意味着您将被重定向到目标 URL,并且在那里的任何回发都将成功发生,因为您已完全转移到那里。
但是,RedirectAndPOST
方法是如何实现所需技巧的呢?!
/// <summary>
/// POST data and Redirect to the specified url using the specified page.
/// </summary>
/// <param name="page">The page which will be the referrer page.</param>
/// <param name="destinationUrl">The destination Url to which
/// the post and redirection is occuring.</param>
/// <param name="data">The data should be posted.</param>
/// <Author>Samer Abu Rabie</Author>
public static void RedirectAndPOST(Page page, string destinationUrl,
NameValueCollection data)
{
//Prepare the Posting form
string strForm = PreparePOSTForm(destinationUrl, data);
//Add a literal control the specified page holding
//the Post Form, this is to submit the Posting form with the request.
page.Controls.Add(new LiteralControl(strForm));
}
正如您所见……我们准备了 POST 表单,这是一个 HTML 表单,其中包含您 POST 数据的隐藏字段和一个脚本标签,该标签包含表单的提交操作……它将在回发后立即执行。
以下是 PreparePOSTForm
方法的代码
/// <summary>
/// This method prepares an Html form which holds all data
/// in hidden field in the addetion to form submitting script.
/// </summary>
/// <param name="url">The destination Url to which the post and redirection
/// will occur, the Url can be in the same App or ouside the App.</param>
/// <param name="data">A collection of data that
/// will be posted to the destination Url.</param>
/// <returns>Returns a string representation of the Posting form.</returns>
/// <Author>Samer Abu Rabie</Author>
private static String PreparePOSTForm(string url, NameValueCollection data)
{
//Set a name for the form
string formID = "PostForm";
//Build the form using the specified data to be posted.
StringBuilder strForm = new StringBuilder();
strForm.Append("<form id=\"" + formID + "\" name=\"" +
formID + "\" action=\"" + url +
"\" method=\"POST\">");
foreach (string key in data)
{
strForm.Append("<input type=\"hidden\" name=\"" + key +
"\" value=\"" + data[key] + "\">");
}
strForm.Append("</form>");
//Build the JavaScript which will do the Posting operation.
StringBuilder strScript = new StringBuilder();
strScript.Append("<script language="'javascript'">");
strScript.Append("var v" + formID + " = document." +
formID + ";");
strScript.Append("v" + formID + ".submit();");
strScript.Append("</script>");
//Return the form and the script concatenated.
//(The order is important, Form then JavaScript)
return strForm.ToString() + strScript.ToString();
}
代码非常简单……对于您想要 POST 的每个键值,我们创建一个隐藏字段,创建一个表单,然后添加必要的脚本来自动提交,通过 JavaScript 代码调用 vPostForm.submit()
。
关注点
为什么将生成的表单和脚本放入一个 Literal 控件并将该控件添加到请求的页面中?! 嗯……这个想法基本上是创建一个能够自动提交值而无需触发的表单……我们创建了该表单及其脚本……现在我们需要让它运行……我们通过将这段代码添加到 Literal
控件来实现,该控件的内容将被显示,不带任何标签!是的……不带标签……这就是技巧……所以表单和脚本将在请求中显示。现在,由于我们正在回发到同一页面……内容(我们的表单和脚本)将成功运行。当它运行时,提交过程发生,并且我们的数据与指定的目标 URL 发生自动回发 :)
如果有人问我这个解决方案的缺点,我会说也许有一个缺点……那就是如果客户端在浏览器中禁用了 JavaScript……那么这个将不起作用。我还没有尝试过……但我预计是这样……对我来说,这是一个重要的进步和一个重要的解决方案,它极大地帮助我解决了许多问题 :)
我希望我的解释足够清晰……因为它需要对 ASP.NET 页面生命周期以及对页面的回发概念有深入的理解。
历史
- 创建了文章,版本 1.0。