The correct way to skip page execution after Response.RedirectToRoute

I am writing an asp.net 4.5 application using the new routing features. I have a page that displays information about an item. In the Page_Load event, I check the route data (element identifier) ​​and user permissions, and if something is wrong (for example, the identifier for the deleted element), I use Response.RedirectToRoute to send their packaging back to the home page. Do not go GO, do not take $ 200.

This made perfect sense until I tried to access the deleted item and instead of the homepage I got an error page. I did some digging and found that even after using RedirectToRoute (unlike the standard Redirect method), the rest of the page code continues to execute , which at least seems wasteful (since I'm just going to throw away the results) and throws errors when the necessary data does not exist .

I did a bit more SO mining and discovered an incredible evil that Response.End() . It does what I need, but even the MSDN page tells me that Response.End is a bastard child of an ancient damned language and is not suitable to see the light of day. The main objection seems to be the fact that Response.End throws an exception and that this is bad for performance. I am not the most experienced developer, so I do not completely understand this problem, but I find it hard to believe that an exception is more expensive than downloading the entire web page. Workarounds seem rather complicated and excessive for the task so simple, especially since most pages require some validation.

What should I do in this situation? Use Response.End and apologize for my insolence? Knock some ugly workaround together? Or is my view of the problem all wrong to start? I really would like to know.

Update: now that I’ve thought about this a bit more, I wonder if I have the wrong perspective of the problem. Perhaps an immediate redirect is not the best answer for the user. Would it be better for me to wrap all the controls in a panel and use something like this?

 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 
+7
source share
2 answers

I can go to a limb without answering the question directly, but I liked to see your update regarding the user experience. I prefer your proposed approach.

I like to give error 410 for id, which are invalid and expand it a bit (translation from 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 
+3
source

RedirectToRoute actually wraps Response.Redirect minus false to complete the request - hence the request continues. You can use HttpApplication.CompleteRequest as an immediate call to complete the request so that the following application events are not triggered.

Response.End (and another redirect change) throws a ThreadAbortException to interrupt the request processing thread, which is really a bad way to stop request processing. In the .NET world, exception handling is always considered expensive because the CLR then needs to search the stack in all directions for exception handling blocks, create a stack trace, etc. IMO, CompleteRequest was introduced in .NET 1.1 to avoid that it actually relies on the installation flag in the ASP.NET infrastructure code to skip further processing except for the EndRequest event.

Another (and best) way is to use Server.Transfer and avoid returning the client to both ends to set up redirection at the same time. The only problem is that the client will not see the redirected URL in the address bar of the browser. I usually prefer this method.

EDIT
CompleteRequest will never work in the case when subsequent events of the page will be called, because the page is a handler, all its events occur in one (and the current) ProcessRequest application. So the only way is to set the flag and check this flag in overrides like Render , PreRender , RaisePostBackEvent , etc.

From a service point of view, it makes sense to have this functionality in the base page class (i.e., support the flag by proposing the CompleteRequest method for subclasses and overriding lifecycle event methods). For example,

 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 } 
+9
source

All Articles