【问题标题】:Proper way to skip page execution after Response.RedirectToRoute在 Response.RedirectToRoute 之后跳过页面执行的正确方法
【发布时间】:2013-01-05 04:24:47
【问题描述】:

我正在使用新的路由功能编写一个 asp.net 4.5 应用程序。我有一个页面显示有关项目的一些信息。在Page_Load 事件中,我检查路线数据(项目 ID)和用户权限,如果有问题(例如,ID 用于已删除的项目),我使用 Response.RedirectToRoute 将它们打包发送回主页。不要通过 GO,不要收取 200 美元。

这很有意义,直到我尝试访问已删除的项目并且我得到一个错误页面而不是主页。我做了一些挖掘,发现即使在我使用RedirectToRoute(与标准的Redirect方法不同)之后,页面代码的其余部分continues to execute,至少看起来很浪费(因为我只是要扔掉结果)并在必要的数据不存在时引发错误。

我做了更多的 SO 挖掘,发现了令人难以置信的 evil,即 Response.End()。它可以满足我的需要,但即使是 MSDN page 也告诉我 Response.End 是一种古老的被诅咒语言的混蛋,不适合看到光明。主要的反对意见似乎是 Response.End 抛出异常,这对性能不利。我不是最有经验的开发人员,所以我不完全理解这个问题,但我很难相信抛出异常比加载整个网页更昂贵。对于如此简单的任务,workarounds 似乎相当复杂和过度,尤其是因为大多数页面都需要某种有效性检查。

在这种情况下我应该怎么做?使用Response.End 并为我的无礼请求原谅?拼凑一些丑陋的解决方法?还是我对这个问题的看法一开始就错了?我真的很想知道。

更新:现在我已经考虑了更多,我想知道我是否对这个问题有错误的看法。也许立即重定向并不是用户体验的最佳响应。将所有控件包装在面板中并使用类似的东西会更好吗?

Private Sub Page_Init(sender As Object, e As EventArgs) Handles Me.Init
    'Validation Code
    If notValid Then
        ControlsPanel.Visible = false
        ErrorPanel.Visible = true
    End If
End Sub

【问题讨论】:

    标签: asp.net vb.net webforms user-experience response.redirect


    【解决方案1】:

    RedirectToRoute 实际上是包装 Response.Redirect 传递 false 以结束请求 - 因此,请求继续。您可以使用HttpApplication.CompleteRequest 作为立即调用来终止请求,这样就不会调用下一个应用程序事件。

    Response.End(和其他重定向变体)抛出ThreadAbortException 以中止请求处理线程,这确实是停止请求处理的糟糕方式。在 .NET 世界中,异常处理总是被认为是昂贵的,因为 CLR 需要一直向上搜索堆栈以查找异常处理块、创建堆栈跟踪等。IMO,CompleteRequest 在 .NET 1.1 中被引入以避免相同的实际上依赖于在 ASP.NET 基础结构代码中设置标志来跳过除EndRequest 事件之外的进一步处理。

    另一种(更好的)方法是使用Server.Transfer 并避免客户端往返以一起设置重定向。唯一的问题是客户端不会在浏览器地址栏中看到重定向的 URL。我通常更喜欢这种方法。

    编辑
    CompleteRequest 永远不会在页面情况下工作,因为页面是一个处理程序,它的所有事件都发生在单个(和当前)内,后续页面事件仍将被调用应用程序事件ProcessRequest。因此,唯一的方法似乎是设置一个标志并在 RenderPreRenderRaisePostBackEvent 等覆盖中检查该标志。

    从维护的角度来看,在基页类中具有这样的功能是有意义的(即维护标志,为子类提供CompleteRequest 方法并覆盖生命周期事件方法)。例如,

    internal class PageBase: System.Web.UI.Page
    {
        bool _requestCompleted;
    
        protected void CompleteRequest()
        {
           Context.ApplicationInstance.CompleteRequest();
           _requestCompleted = true;
        }
    
        protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl,
        string eventArgument)
        {
           if (_requestCompleted) return;
           base.RaisePostBackEvent(sourceControl, eventArgument);
        }
    
        protected internal override void Render(HtmlTextWriter writer)
        {
           if (_requestCompleted) return;
           base.Render(writer);   
        }
    
        protected internal override void OnPreRender(EventArgs e)
        {
           if (_requestCompleted) return;
           base.OnPreRender(e);   
        }
    
        ... and so on
    }
    

    【讨论】:

    • 感谢您的回答。我注意到CompleteRequest 被提议为更好的选择,但它也继续执行页面上的所有代码。页面上的各种控件从项目中获取它们的数据,当它不存在时它们会抛出错误,所以我得到一个错误页面而不是重定向。我可以检查每种方法的有效性,但这似乎相当不必要且不易维护。
    • 我觉得你提到Server.Transfer 很有趣。虽然我很欣赏避免往返的好处,但我觉得这种方法不适用于我的目的,因为用户的当前 URL 无效。我还读到 Server.Transfer 调用 Response.End 并且我没有看到函数重载来避免这种情况。
    • @probackpacker,请接受我的道歉! CompleteRequest 影响应用程序管道事件,但当前处理程序事件会继续。除了使用标志在页面内检查之外别无他法 - 从维护的角度来看,您可以将其提取到可重用的基页面类中(请参阅我的编辑)。
    • @probackpacker,"return" 或 "return 0",在 CompleteRequest 之后,将阻止当前事件处理程序继续进行。
    • 我在 IHttpHandler 中使用context.Response.Redirect。代码:[WebService(Namespace = "http://portalxxx")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class ssoToExternalApp : IHttpHandler { public void ProcessRequest(HttpContext context)
    【解决方案2】:

    我可能会因为不直接回答问题而陷入困境,但我喜欢看到您关于用户体验的更新。我更喜欢你建议的方法。

    我喜欢为无效的 id 提供 410 错误,并使用(从 C# 翻译)对其进行扩展:

    Protected Sub ItemDoesNotExist()
    'item does not exist, serve up error page
    ControlsPanel.Visible = False
    ErrorPanel.Visible = True
    
    'add meta tags for noindex
    Dim mymeta As New HtmlMeta()
    mymeta.Name = "robots"
    mymeta.Content = "noindex"
    Page.Header.Controls.Add(mymeta)
    
    'RESPOND WITH A 410
    Response.StatusCode = 410
    Response.Status = "410 Gone"
    Response.StatusDescription = "Gone"
    Response.TrySkipIisCustomErrors = True
    'important for IIS7, otherwise the Custom error page for 404 shows.
    Page.Title = "item gone"
    End Sub
    

    【讨论】:

    • 感谢您的跟进。我没有意识到您可以使用错误代码进行响应并仍然加载页面。以后我一定会牢记这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-27
    • 2015-07-19
    • 2021-05-02
    • 2020-01-15
    • 1970-01-01
    相关资源
    最近更新 更多