【问题标题】:Post-Redirect-Get with ASP.NET使用 ASP.NET 进行重定向后获取
【发布时间】:2011-07-19 20:45:34
【问题描述】:

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

按钮点击执行一些处理:

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

用户点击按钮,航天器发射,网页重新显示。如果用户按 F5,他们会收到警告:

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

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


问题围绕以下问题展开:

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

换句话说:你如何在 ASP.net 中进行 Post-Redirect-Get?

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

另见

【问题讨论】:

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


    【解决方案1】:

    通常,您会通过创建一个使用查询字符串来指示要加载/处理的记录的 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());
    }
    

    基本上应该是这样的。重定向将导致浏览器对 Redirect(...) 中的 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)
    

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

    “你发给我的那个帖子请求,我已经完成了。现在请到这里的另一个页面......”

    url实际上指向同一个页面这一事实并不重要。


    对您的问题要点列表的一些思考:

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

    您可以通过在表单上设置action 属性来做到这一点,也可以在按钮上设置PostBackUrl

    • 当您发布到不支持的表单时,ViewState 会发生什么变化 读取视图状态?

    视情况而定。如果您只是将表单发布到不同的页面,您可以使用 指令来告诉“新”页面来自哪里。这将简化处理新页面上发布的数据。见this link for details

    • 当您重定向到“真实”aspx 时,ViewState 会变成什么 网络表单?

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

    • ViewState 与 ASP.net 根本不兼容 重定向后获取?

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

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

    没有。见上面的例子。

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

    响应。重定向(网址)。这将向浏览器发送响应,告诉它执行新的获取请求。

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

    当您完成处理发布请求所需的所有工作时。

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

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

    • 一个相关问题提到了 Server.Transfer。使用 Server.Transfer 是 完全错误,并且无法解决 Post-Redirect-Get 问题 (因为没有重定向)。正确吗?

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

    • 必须在 aspx 或 aspx.cs 文件中进行哪些代码更改才能支持 PRG?想必,最起码得把代码改成post MyPage.aspx 之外的某个地方。

    不,可以使用常规回发。诀窍是在页面上有一个(或几个)特定的事件处理程序,在处理发布的数据后执行 Repsonse.Redirect。

    【讨论】:

      【解决方案2】:

      Q) 如何执行 POST 到不是其原始形式的地方?

      A) 使用 PRG,您不会发布到不同的页面,而是回发到同一页面(请参阅您链接到的维基百科页面上的图表。)但是该页面的响应必须是 30X 响应(通常是 302。)

      问)当您发布到不读取视图状态的表单时,ViewState 会变成什么?

      A) POST 时存在视图状态,但是对于您正在执行 GET 的新页面,视图状态将不存在。

      问)当您重定向到“真正的”aspx Web 表单时,ViewState 会变成什么?

      A) 如上所述,没有更多的视图状态被重定向到页面。

      Q) ViewState 与 ASP.net 根本不兼容吗?

      A) ViewState 与 ASP.NET 不兼容。 P/R/G 用于渲染您被重定向到的页面(大多数情况下)是无用的。

      Q) ASP.net 从根本上与 Post-Redirect--Get 不兼容吗?

      A) 否 - 但您不能过分依赖使用一页并将所有状态保持在视图状态,如上所述。也就是说,ASP.MVC 对 P/R/G 的映射要好得多

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

      A) old_page_you_are_posting_from.aspx的bbLaunch_Click方法中的Response.Redirect("new_page_you_are_redirecting_to.aspx")

      Q)如何(即什么 url)重定向到“真正的”aspx 网络表单?一个关系问题提到了 Response.Redirect(Request.RawUrl);

      A) 见上文

      Q) 您何时(即在什么事件处理程序中)重定向到“真正的”aspx Web 表单?

      A) 在您处理完按钮按下后,将数据保存到 DB(或会话等),并且在您将任何其他内容写入响应流之前。

      Q) 相关问题引发了您如何发布表单数据的问题。这意味着不能使用 HTML 表单 - 所有表单数据都必须添加到查询字符串中。这是真的?

      A) 否 - 在 ASP.NET WebForms 中按下按钮将 POST 回页面。

      问)如果是这样,为什么?如果不是,为什么不呢?

      A) 它比这更简单,为什么不呢。成像两个页面:first_page.asp 和 second_page.aspx。 First_page.aspx 上有按钮(以及用户填写的其他 ASP.NET Web 控件,如文本框等)。当他们按下按钮时,将对 first_page.aspx 进行 POST。处理完数据(可能在视图状态中,尽管这已被抽象出来)之后,您使用 Response.redirect 将用户重定向到 second_page.aspx。 second_page.aspx 可以显示你想要的。如果您希望(或需要)显示类似于 first_page.aspx 上的 UI,包括控件及其输入的内容,您可能希望将其存储在会话、cookie、URL 作为查询字符串参数中,以设置second_page.aspx 上的那些控件。 (但您可能不需要在 second_page.aspx 上显示与 first_page.aspx 类似的任何内容 - 因此这里没有一般规则。)

      Q) 浏览器能否将表单数据放入查询字符串中?

      A) 是的,如果您将方法设置为 GET 而不是 POST。您不能覆盖 WebForms 来执行此操作,PRG 不需要这样做

      Q) 一个相关问题提到了 Server.Transfer。使用 Server.Transfer 是完全错误的,并且无法解决 Post-Redirect-Get 问题(因为没有 Redirect)。对吗?

      A) 本质上

      Q) 必须在 aspx 或 aspx.cs 文件中进行哪些代码更改才能支持 PRG?据推测,至少必须将代码更改为在 MyPage.aspx 之外的某个位置发布。

      A) 代码仍应回发(如上所述)。但 Mypage.aspx 应重定向到按钮处理程序中的新页面。

      【讨论】:

        【解决方案3】:

        Post-Redirect-Get 模式可用于 Web 表单。我已经展示了如何通过将 MVC NerdDinner 应用程序转换为 Web 窗体 http://navigationnerddinner.codeplex.com/ 来完成此操作。我保持导航细节完全相同,因此有很多 PRG 模式的示例。

        但是,还有另一种方法可以避免 F5/刷新问题。如果将页面包装在 UpdatePanel(ASP.NET Ajax 的一部分)中,则所有回发都将转换为部分页面请求。这意味着当按下 F5 时,它只会刷新原始 GET 请求(因为没有任何后续 POST),因此您不会收到警告。 (注意,如果 JavaScript 被禁用,警告仍然会出现)。

        【讨论】:

        • 感谢您的宝贵建议!我已经使用 WebForms 这么久了,但是当我正在思考 PRG 时,我完全忘记了使用 UpdatePanel 来代替!这样就很轻松的解决了刷新页面的问题!
        【解决方案4】:

        Post Redirect Get 的具体步骤如下:

        您拥有填写数据的表单,并在有效提交 (POST) 后将它们插入数据库,并给它们一个确认 ID,然后您将用户重定向到使用此确认 ID 作为 URL 参数的页面即用作 (GET) 重定向后,每次 F5-refresh 仅读取数据,不会再次插入。

        插入的代码与显示确认的代码不同,您甚至可以将它们制作为不同的页面 - 您可以使用只读文本框制作相同的页面。

        重定向很简单,asp.net的Responce.Redirect函数

        在 POST 和重定向之后,唯一能将你与之前的操作联系起来的就是确认码(不是视图状态)

        这种方法的缺点是实际上不能识别刷新,只是做了一个额外的步骤,使刷新不会再次插入相同的数据——但需要额外的代码来获取数据。

        另一种方法是识别刷新而不进行重定向。通过识别回发时的刷新,您可以避免将相同的数据插入到用户的单个消息中。网上有一些例子,我已经成功实现了。

        一个例子:http://www.codeproject.com/Tips/319955/How-to-prevent-Re-Post-action-caused-by-pressing-b

        【讨论】:

          【解决方案5】:

          您可以调用Response.Redirect 方法转到另一个位置。

          【讨论】:

          • 我如何进入有Response.Redirect的页面?
          【解决方案6】:

          这有几件事。

          1. 设置主页上表单的action属性(我们称之为LaunchForm.aspx)等于“代理”页面的URL( ProxyLaunchForm.aspx)。

          2. (可选)在表单中添加一个名为 redirectUrl 的隐藏输入,并设置告诉 ProxyLaunchForm.aspx 完成后重定向到哪里的 URL执行启动(PRG 的 R 部分)。

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

          4. 随后(也在 Page_Load 中)执行重定向(使用 #2 中的 redirectUrl 或仅使用引用页面 URL):

            Response.Redirect(Request.Params["redirectUrl"] ?? Request.UrlReferrer.AbsoluteUri);

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

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

          【讨论】:

          • 第 4 项 - session 中的存储值与 viewstate 非常不同,因为会话没有持久化(默认情况下)。 20 分钟后(默认情况下)或服务器/虚拟机重新启动或回收时。因此,对于那些几天后重新加载页面的移动设备,您将没有任何数据。 ViewState 永久存储数据 - 或直到页面从浏览器中清除。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-02-24
          • 2019-10-30
          • 2020-08-24
          • 1970-01-01
          • 2010-11-24
          • 2011-06-24
          • 2011-02-13
          相关资源
          最近更新 更多