重定向后 - 使用ASP.NET获取

时间:2011-03-21 17:14:06

标签: asp.net post-redirect-get redirect-after-post

如何使用ASP.NET实现Post-Redirect-Get模式?

点击按钮会执行一些处理:

<asp:Button id="bbLaunch" OnCommand="bbLaunch_Click" />

用户点击按钮,启动航天器,重新显示网页。如果用户按下F5,则会收到警告:

enter image description here

问题的解决方案是Post-Redirect-Get模式。

在ASP.NET中实现PRG的方法是什么?


问题围绕以下问题:

  • <asp:Button>如何对不是原始形式的地方执行 POST
  • 当您发布到不读取视图状态的表单时 ViewState 会变成什么?
  • 当您重定向到“真正的”aspx网络表单时, ViewState 会变成什么?
  • ViewState 从根本上与 ASP.net不兼容后重定向 - 获取?
  • ASP.net与重定向后 - 获取根本不兼容?
  • 如何将(即什么代码)重定向到“真正的”aspx网络表单?
  • 如何将(即什么网址)重定向到“真正的”aspx网络表单?关系问题提到Response.Redirect(Request.RawUrl);
  • 何时(即在什么事件处理程序中)您是否重定向到“真正的”aspx Web表单?
  • 相关问题引发了如何发布表单数据的问题。这意味着无法使用HTML 表单 - 并且必须将所有表单数据添加到查询字符串中。这是真的?如果是这样,为什么?如果没有,为什么不呢? 可以浏览器将表单数据放入查询字符串中吗?
  • 相关问题提及Server.Transfer。使用Server.Transfer是完全错误的,并且决不会解决Post-Redirect-Get问题(因为没有 Redirect )。正确的吗?
  • 必须在aspxaspx.cs文件中进行哪些代码更改才能支持PRG?据推测,除了post之外,代码必须更改为MyPage.aspx

换句话说:如何在ASP.net中进行重定向后获取?

注意:ASP.net(即不是ASP.net MVC)

另见

6 个答案:

答案 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)

在中间步骤,服务器基本上是这样说:

“您发送给我的帖子请求,我已经完成了。现在请点击这里的其他页面......”

事实上,网址引用同一页并不重要。


一些回应你的问题点列表的思考:

  • 如何对不是它的地方执行POST 原始形式?

您可以在表单上设置action属性,也可以在按钮上设置PostBackUrl

  • 当您发布到没有的表单时,ViewState会变成什么样 阅读视图状态?

取决于。如果您只是将表单发布到其他页面,则可以使用&lt;%@ PreviousPageType ... /&gt;告诉帖子来自的“新”页面的指令。这将简单地使用新页面上的发布数据。请参阅this link for details

  • 当您重定向到“真正的”aspx时,ViewState会变成什么样 网络表格?

查看状态在发布请求中发送。重定向时,浏览器将加载一个新页面,它将创建自己的viestate。

  • 是ViewState从根本上与ASP.net不兼容 后重定向消息得到什么?

取决于你如何看待它。重定向后,新页面将无法访问之前页面的视图状态。

  • 是ASP.net从根本上与Post-Redirect - Get?
  • 不兼容

没有。见上面的例子。

  • 你如何(即什么代码)重定向到“真正的”aspx网络表单?

的Response.Redirect(URL)。这将向浏览器发送响应,告诉它执行新的获取请求。

  • 何时(即在什么事件处理程序中)你重定向到“真正的”aspx 网络表格?

当您执行了处理发布请求所需的所有工作时。

  • 相关问题引发了您发布表单数据的问题。那里 是不能使用HTML表单的含义 - 以及所有表单数据 必须添加到查询字符串中。这是真的?如果是这样,为什么?如果不, 为什么不?浏览器可以将表单数据放入查询字符串吗?

不能很好地支持重定向发布请求,应该避免使用。可以通过使用http响应307完成(使用某些浏览器)。当这样做时,服务器有效地告诉浏览器“我将不会处理您的请求,请将其发布到此其他页面

  • 相关问题提及Server.Transfer。使用Server.Transfer是 完全错误,绝不解决Post-Redirect-Get问题 (因为没有重定向)。正确的吗?

Server.Transfer(...)是在服务器端发生的事情。浏览器不知道它。基本上,一个页面可以使用Server.Transfer,以便让其他页面执行某些处理,并且该页面将负责将响应发送回浏览器。但浏览器会认为这是回复的原始页面。

  • 要支持的aspx或aspx.cs文件中必须进行哪些代码更改 PRG?据推测,至少,代码必须更改为post MyPage.aspx以外的地方。

不,可以使用常规回发。诀窍是在页面上有一个(或几个)特定的事件处理程序,它们在处理发布的数据后执行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)

有一些事情可以解决这个问题。

  1. 在主页面上设置表单的操作属性(让我们称之为 LaunchForm.aspx )等于&的URL #34;代理&#34;页面( ProxyLaunchForm.aspx )。

      

    &lt; form id =&#34; form1&#34; RUNAT =&#34;服务器&#34;行动=&#34; ProxyLaunchForm.aspx&#34;方法=&#34; POST&#34;&GT;

  2. (可选)将名为 redirectUrl 的隐藏输入添加到表单中,并将值告知 ProxyLaunchForm.aspx 一旦完成后重定向的位置执行发布(PRG的R部分)。

  3. 现在在 ProxyLaunchForm.aspx 上,实现应该在 Page_Load 事件处理程序中进行,因为它可以访问表单发布数据。在这里执行发布。

  4. 随后(也在 Page_Load 中)执行重定向(使用#2中的 redirectUrl ,或者只使用引用页面网址):

      

    Response.Redirect(Request.Params [&#34; redirectUrl&#34;] ?? Request.UrlReferrer.AbsoluteUri);

    还有View State的问题。我认为解决这个问题的最简单方法是改变视图状态的持久化方式。通常,它被保存到页面上的隐藏输入元素中并在回发时检索(这当然意味着由于HTTP的无状态特性,它将在重定向后丢失)。但是,您可以覆盖ASP.net使用的方法,并让它使用 Session (这样即使在PRG代理操作之后它仍然存在)。所以,最后......

  5. LaunchForm.aspx.cs 中,将 SavePageStateToPersistenceMedium 的子类 Page 用作基类LoadPageStateFromPersistenceMedium 方法,用于从会话中存储/检索它们,而不是从隐藏的表单字段中存储/检索它们。见下文(和here's more info on how this works.)。

  6. *

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