在Response.RedirectToRoute之后跳过页面执行的正确方法

时间:2013-01-22 01:22:23

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

我正在使用新的路由功能编写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

2 个答案:

答案 0 :(得分:10)

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

Response.End(和其他重定向变体)抛出ThreadAbortException以中止请求处理线程,这是一种方式来停止请求处理。在.NET世界中,异常处理总是被认为是昂贵的,因为CLR需要在异常处理块中一直向上搜索堆栈,创建堆栈跟踪等。在.NET 1.1中引入了IMO CompleteRequest以避免实际上依赖于在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
}

答案 1 :(得分:3)

我可能会因为没有直接回答问题而走出困境,但我喜欢看到有关用户体验的更新。我更喜欢你建议的方法。

我喜欢为无效的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