如何使用ASP.NET实现Post-Redirect-Get模式?
点击按钮会执行一些处理:
<asp:Button id="bbLaunch" OnCommand="bbLaunch_Click" />
用户点击按钮,启动航天器,重新显示网页。如果用户按下F5,则会收到警告:
问题的解决方案是Post-Redirect-Get模式。
在ASP.NET中实现PRG的方法是什么?
问题围绕以下问题:
<asp:Button>
如何对不是原始形式的地方执行 POST
?Response.Redirect(Request.RawUrl);
Server.Transfer
。使用Server.Transfer
是完全错误的,并且决不会解决Post-Redirect-Get问题(因为没有 Redirect )。正确的吗?aspx
或aspx.cs
文件中进行哪些代码更改才能支持PRG?据推测,除了post
之外,代码必须更改为MyPage.aspx
。换句话说:如何在ASP.net中进行重定向后获取?
注意:ASP.net(即不是ASP.net MVC)
答案 0 :(得分:35)
通常,您可以通过创建一个aspx Web表单来执行此操作,该表单使用查询字符串来指示要加载/处理的记录。
假设您有一个页面让您更新一些客户信息:
http://www.mysite.com/customer.aspx
您将使用查询字符串中的ID加载表单:
http://www.mysite.com/customer.aspx?CustomerId=42
在代码隐藏中你会有这样的东西:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
int customerId = 0;
if (!string.IsNullOrEmpty(Request.QueryString["CustomerId"]))
{
int.TryParse(Request.QueryString["CustomerId"], out customerId );
}
if (customerId == 0)
{
//handle case when no valid customer id was passed in the qs here
}
else
{
//load customer details, bind controls etc
//make sure to handle the case when no customer was found using the id in the qs
}
}
}
然后在您的页面中的某个位置,您将有一个保存更改的按钮。该按钮在后面的代码中有一个OnClick处理程序:
protected void SaveClicked(object sender, EventArgs e)
{
//save changes to database here
//Redirect if all went well
Response.Redirect("http://www.mysite.com/customer.aspx?CustomerId="
+ idOfSavedCustomer.ToString());
}
基本上应该是它。重定向将导致浏览器在重定向(...)中为URL发出新的GET请求。它将加载页面,if (!IsPostBack)
将运行并使用您刚刚在上一篇文章中保存的新值初始化页面。
对于整个过程,浏览器和服务器之间的流量看起来像这样:
Browser: GET http://www.mysite.com/customer.aspx?CustomerId=42
Server: 200 (send back some html)
Browser: POST http://www.mysite.com/customer.aspx?CustomerId=42 (post data sent in request)
Server: 302 (point to http://www.mysite.com/customer.aspx?CustomerId=42)
Browser: GET http://www.mysite.com/customer.aspx?CustomerId=42
Server: 200 (send html)
在中间步骤,服务器基本上是这样说:
“您发送给我的帖子请求,我已经完成了。现在请点击这里的其他页面......”
事实上,网址引用同一页并不重要。
一些回应你的问题点列表的思考:
您可以在表单上设置action
属性,也可以在按钮上设置PostBackUrl
。
取决于。如果您只是将表单发布到其他页面,则可以使用&lt;%@ PreviousPageType ... /&gt;告诉帖子来自的“新”页面的指令。这将简单地使用新页面上的发布数据。请参阅this link for details。
查看状态在发布请求中发送。重定向时,浏览器将加载一个新页面,它将创建自己的viestate。
取决于你如何看待它。重定向后,新页面将无法访问之前页面的视图状态。
没有。见上面的例子。
的Response.Redirect(URL)。这将向浏览器发送响应,告诉它执行新的获取请求。
当您执行了处理发布请求所需的所有工作时。
不能很好地支持重定向发布请求,应该避免使用。可以通过使用http响应307完成(使用某些浏览器)。当这样做时,服务器有效地告诉浏览器“我将不会处理您的请求,请将其发布到此其他页面”
Server.Transfer(...)是在服务器端发生的事情。浏览器不知道它。基本上,一个页面可以使用Server.Transfer,以便让其他页面执行某些处理,并且该页面将负责将响应发送回浏览器。但浏览器会认为这是回复的原始页面。
不,可以使用常规回发。诀窍是在页面上有一个(或几个)特定的事件处理程序,它们在处理发布的数据后执行Repsonse.Redirect。
答案 1 :(得分:7)
问)如何对不是原始形式的地方执行POST?
A)使用PRG,你不会发布到另一个页面,你回发到同一页面(参见你链接到的维基百科页面上的图表。)但是该页面的响应必须是30倍响应(通常是302。)
问:当您发布到不读取视图状态的表单时,ViewState会变成什么?
A)当您进行POST时,视图状态就在那里,但是您正在进行GET操作的新页面中将不存在视图状态。
问:当您重定向到“真正的”aspx Web表单时,ViewState会变成什么?
A)上面,没有更多的视图状态重定向到页面。
问)ViewState是否与ASP.net根本不兼容?
A)ViewState与ASP.NET不兼容。 P / R / G(用于渲染您重定向到的页面)(大部分)无用。
问)ASP.net与Post-Redirect根本不兼容 - Get?
A)否 - 但是你不能过度依赖使用一个页面并将所有状态保持在viewstate中,如上所述。也就是说,ASP.MVC比P / R / G更好地映射
问)你如何(即什么代码)重定向到“真正的”aspx网络表单?
A)old_page_you_are_posting_from.aspx的bbLaunch_Click方法中的Response.Redirect(“new_page_you_are_redirecting_to.aspx”)
问)你如何(即什么网址)重定向到“真正的”aspx网络表单?关系问题提到Response.Redirect(Request.RawUrl);
A)见上文
Q)何时(即在什么事件处理程序中)您重定向到“真正的”aspx Web表单?
A)按下按钮后,将数据保存到数据库(或会话等),然后再将其他任何内容写入响应流。
问)相关问题会引发您发布表单数据的问题。这意味着无法使用HTML表单 - 并且必须将所有表单数据添加到查询字符串中。这是真的?
A)否 - ASP.NET WebForms中的按钮按下将回到该页面。
问)如果是这样,为什么?如果没有,为什么不呢?
A)比这更简单,为什么不呢。成像两页:first_page.asp和second_page.aspx。 First_page.aspx上有按钮(以及用户填写的其他ASP.NET Web控件,如文本框等。)当他们按下按钮时,会对first_page.aspx进行POST。在处理完数据后(可能在viewstate中,虽然这是抽象的),您可以使用Response.redirect将用户重定向到second_page.aspx。 Second_page.aspx可以显示您想要的内容。如果您希望(或需要)显示与first_page.aspx类似的UI,包括控件及其输入内容,您可能希望将其存储在会话中,cookie,URL作为查询字符串参数,以设置second_page.aspx上的那些控件。 (但你可能不需要在second_page.aspx上显示类似于first_page.aspx的任何内容 - 所以这里没有一般规则。)
问)浏览器可以将表单数据放入查询字符串吗?
A)是的,如果您将方法设置为GET而不是POST。您不能覆盖WebForms来执行此操作,PRG
不需要这样做问)相关问题提及Server.Transfer。使用Server.Transfer是完全错误的,并且决不会解决Post-Redirect-Get问题(因为没有重定向)。正确的吗?
A)基本上
问:在aspx或aspx.cs文件中必须进行哪些代码更改才能支持PRG?据推测,至少,代码必须更改为在MyPage.aspx之外的某处发布。A)代码仍然应该回发(如上所述。)但是Mypage.aspx应该重定向到按钮处理程序中的新页面。
答案 2 :(得分:5)
Post Redirect Get的确切步骤是:
您拥有使用数据填写的表单,并在有效提交(POST)后将它们插入数据库,并为其提供确认ID,然后将用户重定向到具有此确认ID作为URL参数的页面用作(GET)重定向后,每次F5刷新只读取数据而不再插入数据。
插入代码与显示确认的代码不同,您甚至可以将它们设为不同的页面 - 您可以使用只读文本框创建相同的页面。
重定向很简单,就是asp.net的Responce.Redirect
功能
在POST和重定向之后,唯一认为连接前一个操作的是确认代码(不是viewstate)
这种方法的减号实际上是无法识别刷新,只是做了一个额外的步骤,使刷新不再插入相同的数据 - 但需要额外的代码来获取数据。
另一种方法是识别刷新而不是重定向。通过识别回发后的刷新,您可以避免将单个消息中的相同数据插入用户。互联网上有一些例子,我已经成功实施了一个。
一个例子:http://www.codeproject.com/Tips/319955/How-to-prevent-Re-Post-action-caused-by-pressing-b
答案 3 :(得分:5)
Post-Redirect-Get模式可以在Web窗体中使用。我已经展示了如何通过将MVC NerdDinner应用程序转换为Web窗体http://navigationnerddinner.codeplex.com/来完成此操作。我保持导航细节完全相同,因此有很多PRG模式的例子。
但是,还有另一种方法可以避免F5 / Refresh问题。如果将页面包装在UpdatePanel(ASP.NET Ajax的一部分)中,则所有回发都将转换为部分页面请求。这意味着当按下F5时,它只会刷新原始的GET请求(因为没有任何后续的POST),因此您不会收到警告。 (注意,如果禁用JavaScript,则仍会显示警告。)
答案 4 :(得分:4)
您可以调用Response.Redirect方法转到其他位置。
答案 5 :(得分:2)
有一些事情可以解决这个问题。
在主页面上设置表单的操作属性(让我们称之为 LaunchForm.aspx )等于&的URL #34;代理&#34;页面( ProxyLaunchForm.aspx )。
&lt; form id =&#34; form1&#34; RUNAT =&#34;服务器&#34;行动=&#34; ProxyLaunchForm.aspx&#34;方法=&#34; POST&#34;&GT;
(可选)将名为 redirectUrl 的隐藏输入添加到表单中,并将值告知 ProxyLaunchForm.aspx 一旦完成后重定向的位置执行发布(PRG的R部分)。
现在在 ProxyLaunchForm.aspx 上,实现应该在 Page_Load 事件处理程序中进行,因为它可以访问表单发布数据。在这里执行发布。
随后(也在 Page_Load 中)执行重定向(使用#2中的 redirectUrl ,或者只使用引用页面网址):
Response.Redirect(Request.Params [&#34; redirectUrl&#34;] ?? Request.UrlReferrer.AbsoluteUri);
还有View State的问题。我认为解决这个问题的最简单方法是改变视图状态的持久化方式。通常,它被保存到页面上的隐藏输入元素中并在回发时检索(这当然意味着由于HTTP的无状态特性,它将在重定向后丢失)。但是,您可以覆盖ASP.net使用的方法,并让它使用 Session (这样即使在PRG代理操作之后它仍然存在)。所以,最后......
在 LaunchForm.aspx.cs 中,将 SavePageStateToPersistenceMedium 和的子类 Page 用作基类LoadPageStateFromPersistenceMedium 方法,用于从会话中存储/检索它们,而不是从隐藏的表单字段中存储/检索它们。见下文(和here's more info on how this works.)。
*
public class PersistViewStateToSession : Page
{
protected override void SavePageStateToPersistenceMedium(object viewState)
{
// serialize the view state into a base-64 encoded string
LosFormatter los = new LosFormatter();
StringWriter writer = new StringWriter();
los.Serialize(writer, viewState);
// save the string to session
Session["LaunchViewState"] = writer.ToString();
}
protected override object LoadPageStateFromPersistenceMedium()
{
if (!Session["LaunchViewState"] == null)
return null;
else
{
string sessionString = (string)Session["LaunchViewState"];
// deserialize the string
LosFormatter los = new LosFormatter();
return los.Deserialize(viewStateString);
}
}
}