locked
Error Handling in global.asax RRS feed

  • Question

  • User-1021195338 posted

    Hi all,

    I'm currently trying to centralize error handling in the global.asax in my asp.net MVC app. It has the following code to display a custom Error view in the default location (~/Views/Shared/Error.aspx) without using the HandleError attribute in the Controllers and specifying the <customErrors> node in the web.config.

    The code works under the following conditions:

    - Visual studio 2008 built-in development Web Server

    - IIS 6 with wildcard script mapping.

    - IIS 7 with classic pipeline mode and wildcard script mapping.

     

    The code does not work under the following conditions:

    - IIS 7 with integrated pipeline mode.

     

    My question is how can I setup IIS 7 or my MVC app to be hosted in the IIS7 integrated pipeline mode? Thank you all for your help!

     

    My machine configuration: Windows 7, IIS7, Visual Studio 2008 SP1, ASP.NET MVC 1.0

     

    My global.asax.cs code

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace myApp {
        // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
        // visit http://go.microsoft.com/?LinkId=9394801
    
        public class MvcApplication : System.Web.HttpApplication {
            public static void RegisterRoutes(RouteCollection routes) {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                routes.MapRoute(
                    "Default",                                              // Route name
                    "{controller}/{action}/{id}",                           // URL with parameters
                    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
                );
    
            }
    
            protected void Application_Start() {
                RegisterRoutes(RouteTable.Routes);
            }
    
            protected void Application_Error() {
                HttpContext ctx = HttpContext.Current;
                KeyValuePair<string, object> error = new KeyValuePair<string, object>("ErrorMessage", ctx.Server.GetLastError().ToString());
                ctx.Response.Clear();
                RequestContext rc = ((MvcHandler)ctx.CurrentHandler).RequestContext;
                string controllerName = rc.RouteData.GetRequiredString("controller");
                IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
                IController controller = factory.CreateController(rc, controllerName);
                ControllerContext cc = new ControllerContext(rc, (ControllerBase)controller);
    
                ViewResult viewResult = new ViewResult { ViewName = "Error" };
                viewResult.ViewData.Add(error);
                viewResult.ExecuteResult(cc);
                ctx.Response.End();
            }
        }
    }
    


     


    Jack

    Friday, December 18, 2009 3:50 PM

Answers

  • User-1021195338 posted

    I found out i was only missing a line of code (ctx.Server.ClearError();) to get it to display my custom error view intead of the default asp.net yellow screen of death. This code in the global.asax.cs will work on IIS6 and on IIS7 with the default app pool in the integrated pipeline mode.

    So no fussing with or need to do any of the following: 

    - decorating controllers or action methods with the HandleError attribute.

    - subclassing every controller in a base controller class that overrides the OnException event handler just to handle errors.  

    - setting the customErrors node in the web.config to specify the mode and defaultRedirect attributes.

    - setting Classic .NET AppPool in IIS7 and add a wildcard script mapping, which i read may have some performance implications.

     

    Here's the code I settled on:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace myApp {
    
        public class MvcApplication : System.Web.HttpApplication {
            public static void RegisterRoutes(RouteCollection routes) {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                routes.MapRoute(
                    "Default",                                              // Route name
                    "{controller}/{action}/{id}",                           // URL with parameters
                    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
                );
            }
    
            protected void Application_Start() {
                RegisterRoutes(RouteTable.Routes);
            }
    
            protected void Application_Error() {
                HttpContext ctx = HttpContext.Current;
                KeyValuePair<string, object> error = new KeyValuePair<string, object>("ErrorMessage", ctx.Server.GetLastError().ToString());
                ctx.Response.Clear();
                RequestContext rc = ((MvcHandler)ctx.CurrentHandler).RequestContext;
                string controllerName = rc.RouteData.GetRequiredString("controller");
                IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
                IController controller = factory.CreateController(rc, controllerName);
                ControllerContext cc = new ControllerContext(rc, (ControllerBase)controller);
    
                ViewResult viewResult = new ViewResult { ViewName = "Error" };
                viewResult.ViewData.Add(error);
                viewResult.ExecuteResult(cc);
                ctx.Server.ClearError();
                //ctx.Response.End();
    
            }
        }
    }
    


     Thank you all and cheers!

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, December 22, 2009 3:32 PM

All replies

  • User-1005219520 posted

    Why don't you want to use 

    <customErrors mode="RemoteOnly" defaultRedirect="~/Error">
    	<error statusCode="403" redirect="~/Error" />
    	<error statusCode="404" redirect="~/Error" />
    </customErrors>
    


     

    Friday, December 18, 2009 6:44 PM
  • User-1021195338 posted

     Thanks Ricka6. We're trying to avoid using customErrors in the web.config and decorating controllers with the HandlerError attribute. I tried subclassing all controllers in a custom controller base class that overrides the OnException method as follows. It works in all situations, but this being that it will catch only errors in controllers. It would be nice to be able to show the error view from the global.asax's Error event handler itself.

    Thanks again.

    protected override void OnException(ExceptionContext filterContext) { 
       
    filterContext.ExceptionHandled = true;       
    this.View("Error").ExecuteResult(this.ControllerContext); 
      
    }


     

     

     

      

    Saturday, December 19, 2009 9:51 AM
  • User-1021195338 posted

    I found out i was only missing a line of code (ctx.Server.ClearError();) to get it to display my custom error view intead of the default asp.net yellow screen of death. This code in the global.asax.cs will work on IIS6 and on IIS7 with the default app pool in the integrated pipeline mode.

    So no fussing with or need to do any of the following: 

    - decorating controllers or action methods with the HandleError attribute.

    - subclassing every controller in a base controller class that overrides the OnException event handler just to handle errors.  

    - setting the customErrors node in the web.config to specify the mode and defaultRedirect attributes.

    - setting Classic .NET AppPool in IIS7 and add a wildcard script mapping, which i read may have some performance implications.

     

    Here's the code I settled on:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace myApp {
    
        public class MvcApplication : System.Web.HttpApplication {
            public static void RegisterRoutes(RouteCollection routes) {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                routes.MapRoute(
                    "Default",                                              // Route name
                    "{controller}/{action}/{id}",                           // URL with parameters
                    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
                );
            }
    
            protected void Application_Start() {
                RegisterRoutes(RouteTable.Routes);
            }
    
            protected void Application_Error() {
                HttpContext ctx = HttpContext.Current;
                KeyValuePair<string, object> error = new KeyValuePair<string, object>("ErrorMessage", ctx.Server.GetLastError().ToString());
                ctx.Response.Clear();
                RequestContext rc = ((MvcHandler)ctx.CurrentHandler).RequestContext;
                string controllerName = rc.RouteData.GetRequiredString("controller");
                IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
                IController controller = factory.CreateController(rc, controllerName);
                ControllerContext cc = new ControllerContext(rc, (ControllerBase)controller);
    
                ViewResult viewResult = new ViewResult { ViewName = "Error" };
                viewResult.ViewData.Add(error);
                viewResult.ExecuteResult(cc);
                ctx.Server.ClearError();
                //ctx.Response.End();
    
            }
        }
    }
    


     Thank you all and cheers!

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, December 22, 2009 3:32 PM
  • User-599345941 posted

    Thank you jackh:

    I've used this technique in .net webforms applications for years, but haven't thought of using it in an MVC application.

    I tried handling errors the MVC way by using [HandleError], setting a custom error in web.config, and overriding the controller OnException, but was running into all kinds of problems.

    Your solution works great!!

    Tuesday, December 29, 2009 3:34 PM
  • User141764145 posted

    ctx.Server.ClearError();

    Hey Thanks that really helped on my application on IIS 7.


    Friday, May 7, 2010 6:31 AM
  • User1989633353 posted
    Hi folks, if anyone can help I would appreciate .. I have used the example in this thread and am very hopeful I can get it to work ..
    The following line .. 
    RequestContext rc = ((MvcHandler)ctx.CurrentHandler).RequestContext;
     ... blows up with the following error message .. 
    {"Unable to cast object of type 'System.Web.DefaultHttpHandler' to type 'System.Web.Mvc.MvcHandler'."}

     

    Tuesday, April 5, 2011 12:42 PM
  • User-1021195338 posted

    Hi Chuck,

    Are you trying to use this code on a non ASP.NET MVC project? If so, this code would not work.

    Monday, June 6, 2011 10:57 AM
  • User-1529513071 posted

    Assuming you have defined a default route in your Global.asax

    public static void RegisterRoutes(RouteCollection routes)
    {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                    "Default", // Route name
                    "{controller}/{action}/{id}", // URL with parameters
                    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                    new String[] { "YourNamespace.Controllers" } // Parameter defaults
            );
    }
    
    

    Modify the code from above.

    void Application_Error(Object sender, EventArgs e)
    {
                Exception exception = Server.GetLastError();
                Response.Clear();
    
                HttpException httpException = exception as HttpException;
    
                if (httpException != null)
                {
                    bool notfound;
    
                    switch (httpException.GetHttpCode())
                    {
                        case 404:
                            notfound = true;
                            break;
                        default:
                            notfound = false;
                            break;
                    }
                    Server.ClearError();
                    if(notfound)
                        this.Response.RedirectToRoute("Default", new { controller = "Home", action = "NotFound" });
                    else
                        this.Response.RedirectToRoute("Default", new { controller = "Home", action = "Error" });
                }
    
    }
    
    

    You have to have a HomeController with the ActionMethods "Error" and "NotFound". Remember never return detailed error messages to users in production environments.

    Thursday, October 27, 2011 9:51 AM