Friday, October 12, 2012

Transferring to error pages from Application_Error handler in MVC3

Our task is to implement transferring(not redirection) to correspond error page when error occurs in ASP.NET MVC3 application.

Good solution for redirection to error pages is to use <customErrors> section in web.config. But we need transferring for SEO purposes.

So it was obvious to use httpErrors section instead of customErrors section:
  <httpErrors errorMode="Custom" existingResponse =" Replace" >  
     < clear />  
     < error statusCode =" 403" path= "/Error/Forbidden " responseMode="ExecuteURL" />  
     < error statusCode =" 404" path= "/Error/NotFound " responseMode="ExecuteURL" />  
     < error statusCode =" 500" path= "/Error/InternalError " responseMode="ExecuteURL" />  
   </ httpErrors>  

It's good solution, but it works only when your application is under website root. If your application is under virtual directory transferring will be broken.

The only solution i found is to use Application_Error handler in Global.asax instead of config:
 protected void Application_Error(object sender, EventArgs e)  
       var exception = Server.GetLastError();  
       if (Context.Request.IsLocal)  
       var actionName = "InternalError";  
       if (exception is HttpException)  
         var code = (exception as HttpException).GetHttpCode();  
         switch (code)  
           case 400:  
             actionName = "NotFound";  
           case 403:  
             actionName = "Forbidden";  
           case 404:  
             actionName = "NotFound";  
       var path = string.Format( "~/Error/{0}/" , actionName);        
       Server.TransferRequest(path, false);        

Code is obvious and it works fine almost for all cases. ALMOST, but not for all.

I am sure you know popular ASP.NET exception "A potentially dangerous Request.Form value was detected from the client". This exception happens when you(or ASP.NET) try to extract QueryString values, but there is "potential dangerous" in values: html tags, for example.

So, exception happens, then our Application_Error handler fires. Then we use Server.TransferRequest which PRESERVE QueryString of current request despite on second "false" parameter. Now we have "potential dangerous" again in our QueryString during this transferring, which wll trigger Application_Error again, and so on.  What we get here is cyclical transferring that can kill Application Pool easily.

To force Server.TransferRequest NOT to save QueryString of current request you have to specify QueryString explicitly in your transferring URL. For example:
 var path = string.Format( "~/Error/{0}/?transfer=1" , actionName);   

No comments:

Post a Comment