CodeDigest.Com Logo
Featured:

Exception or Error Handling in Asp.Net MVC Using Custom Error Page with Proper HTTP Status Code

Tagged as: Asp.Net MVC Posted By

Exception handling is one of the primary concerns in any applications we develop. It is also very important that we should not emit any technical details when the application breaks. Asp.Net has inbuilt support to render a custom error page through <CustomErrors> web.config settings when the application throws an exception that is not handled. Configuring this web.config settings, it is very easy to display a generic error page whenever the application breaks. Though this works fine for most the situations, it is not easy to log exception data or to return an appropriate HTTP status code using this approach.

In this article, let’s see how to do custom error or exception handling in Asp.Net MVC 5.0 applications and also return HTTP status codes appropriately. Let’s also return a generic error message when there is an unhandled exception raised during AJAX requests too. Assuming you have an Asp.Net MVC 5.0 project in Visual Studio 2015, let’s incorporate custom error pages and configure the application to display it whenever the application breaks. The complete source code is attached at the end of this article for reference.

For Asp.Net MVC applications, we can handle this in multiple ways. Here, I will discuss one of the most commonly used approach which will handle almost every situations in our Asp.Net MVC applications.

Using OnException() and HandleUnknownAction() Event

The events OnException() and HandleUnknownAction() are exposed by the Controller base class which can be overridden to catch the exceptions raised at Controller and Action level. As the name suggests, the OnException() event will get called whenever there is an unhandled exception occurs in a Controller class. Similarly, the HandleUnknownAction() event will be called whenever the Controller or the Action invoker receives a request for an action method that does not exists. So, we can implement these events to handle all the exception occurring at Controller level.

Instead of repeating the implementation of these events in every Controller, we can define a BaseController class by inheriting the Controller base class and include the implementation at one place. Our application Controllers can now derive from this BaseController class instead of Controller class. The BaseController class code is below.

 

public class BaseController : Controller
{
    protected override void HandleUnknownAction(string actionName)
    {
        this.Invoke404NotFound(HttpContext);
    }

    public ActionResult Invoke404NotFound(HttpContextBase httpContext)
    {
        IController errorController = new ErrorController();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "error");
        errorRoute.Values.Add("action", "pagenotfound");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(httpContext, errorRoute));
        return new EmptyResult();
    }
    protected override void OnException(ExceptionContext filterContext)
    {
        Exception ex = filterContext.Exception;
        //log exception

        if (HttpContext.Request.IsAjaxRequest())
        {
            Response.Write("Ajax request failed due to server error!");
            Response.End();
        }
        else
        {
            IController errorController = new ErrorController();
            var errorRoute = new RouteData();
            errorRoute.Values.Add("controller", "error");
            errorRoute.Values.Add("action", "generalerror");
            errorRoute.Values.Add("url", HttpContext.Request.Url.OriginalString);
            errorController.Execute(new RequestContext(HttpContext, errorRoute));

        }

        filterContext.ExceptionHandled = true;
    }
}

 

For simplicity, I have not included the logging code. You can use log4net or ELMAH or any other logging framework of your choice for logging. The above code executes the pagenotfound() action method in ErrorController if there is request for any unknown action method and generalerror() for any other errors. The above code also handles error raised by Ajax requests by calling the extension method HttpContext.Request.IsAjaxRequest() from Request object to check if it is an AJAX request. For AJAX requests, the above code returns error text as string content. You can return json strings and handle it appropriately at client side.

The ErrorController code below.

 

public class ErrorController : Controller
{  
    public ActionResult pagenotfound()
    {
        Response.StatusCode = (int) HttpStatusCode.NotFound;
        ViewBag.URL = RouteData.Values["url"].ToString();
        return View();
    }
    public ActionResult generalerror()
    {
        Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        ViewBag.Message = "Error occured!";
        return View();
    }
}
 

The above code which handles error using BaseController class handles errors that are raised only at Controller level. When there is an exception occurred outside the Controller’s perspective, for example, if there are no controllers to map the incoming request then we need to include the error handling infrastructure at application level too. To catch these errors, we can use the Application_Error() event in Global.asax file.

The below code does that.

 

protected void Application_Error()
{
    Exception ex = Server.GetLastError();

    Context.Response.Clear();
    Context.ClearError();
    var httpException = ex as HttpException;
    RequestContext requestContext = ((MvcHandler)Context.CurrentHandler).RequestContext;
    if (requestContext.HttpContext.Request.IsAjaxRequest())
    {
        Context.Response.Write("Ajax request failed due to server error!");
        Context.Response.End();
    }
    else
    {
        var routeData = new RouteData();
        routeData.Values["controller"] = "error";
        routeData.Values["action"] = "generalerror";
        routeData.Values.Add("url", Context.Request.Url.OriginalString);
        if (httpException != null)
        {
            switch (httpException.GetHttpCode())
            {
                case 404:
                    routeData.Values["action"] = "pagenotfound";
                    break;
                case 403:
                    routeData.Values["action"] = "generalerror";
                    break;
                case 500:
                    routeData.Values["action"] = "generalerror";
                    break;
            }
        }
        IController controller = new ErrorController();
        controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    }
}
 

So, this completes our custom error handling infrastructure that we need for Asp.Net MVC applications. When executed, you can see 404 not found status using Firebug plugin for unknown action and controller like below.

HTTP Status 404 - Unknown Action

HTTP Status 404 - Unknown Controller

For all other unhandled exceptions, we get 500 status and general error page like below.

HTTP Status 500 - For all other unhandled errors

Download the source and see it in action!



Feedback

Comments