Handle exceptions in ASP.NET + IIS

Handling exceptions in ASP.NET can be quite tricky. There is lot of ways how to handle them, some are described in the links below (see resources to study).

Our solution looks like this

Web.config

<system.web> <!-- MVC Settings -->
    <compilation debug="true" targetFramework="4.5.1" />
    <customErrors mode="On" /> <!-- TODO: Use RemoteOnly for production -->
  </system.web>

  <system.webServer> <!-- IIS Settings -->
    <httpErrors errorMode="Custom" existingResponse="Auto">
      <remove statusCode="404" subStatusCode="-1" />
      <remove statusCode="500" subStatusCode="-1" />
      <error statusCode="404" path="/ErrorPages/404.aspx" responseMode="ExecuteURL" />
      <error statusCode="500" path="/ErrorPages/500.aspx" responseMode="ExecuteURL" />
    </httpErrors>
</system.webServer>

Because we need to log the exceptions for investigation we added filters. If you do not care what actually caused the exception you can skip this step, but I do not recommend it.

First you need to declare the filter (for both MVC/Http  and API Controllers. Yes that means two filters, which are almost the same)

using System.Web.Mvc;
using log4net;

namespace FinaDb.WebUI.Web
{
    public class CustomHandleErrorAttribute : HandleErrorAttribute
    {
        private static readonly ILog _log = LogManager.GetLogger(typeof(StHandleErrorAttribute));

        public override void OnException(ExceptionContext filterContext)
        {
            var controllerName = (string)filterContext.RouteData.Values["controller"];
            var actionName = (string)filterContext.RouteData.Values["action"];

            const string placeholder = "There was an error in controller '{0}' and method '{1}'";
            string message = string.Format(placeholder, controllerName, actionName);

            //Log the message for future investigation.
            //This is the whole purpose of this filter attribute
            _log.Error(message, filterContext.Exception);

            base.OnException(filterContext);
        }
    }
}

And for Web API

using System.Web.Http.Filters;
using log4net;

namespace FinaDb.Web.Api
{
    public class UnhandledApiExceptionAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            base.OnException(context);
            var log = LogManager.GetLogger(context.ActionContext.ControllerContext.ControllerDescriptor.ControllerType);
            log.Error("API error", context.Exception);
        }
    }
}

Register filter for MVC

using System.Web.Mvc;
using FinaDb.WebUI.Web;

namespace FinaDb.WebUI
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
           //That's your filter
           filters.Add(new CustomHandleErrorAttribute());
        }
    }
}

Register filter for Web API

using System.Net.Http.Formatting;
using System.Web.Http;
using FinaDb.Web.Api;

namespace FinaDb.WebUI
{
    //Shortened for brevity, you will probably want to do more, like register routes etc ... 
    public static class WebApiConfig
    {
        public static void Configure(HttpConfiguration config)
        {            
            RegisterGlobalFilters(config);
        }

        public static void RegisterGlobalFilters(HttpConfiguration config)
        {
            //That's your filter
            config.Filters.Add(new UnhandledApiExceptionAttribute());
        }
    }
}

Set appropriate status code in aspx page (otherwise 200 would be returned)

//first line in your 404 aspx page
Response.StatusCode = 404;

That’s basically all. The actual 404.aspx and 500.aspx are not shown here because they are trivial. Basically they just show some user friendly message that something went wrong. (And they look boring, not like the fancy error pages like this)

Resources to study:

http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling

https://code.google.com/p/elmah/ (I did not try that one, but it looks nice)

http://blog.dantup.com/2009/04/aspnet-mvc-handleerror-attribute-custom.html

http://benfoster.io/blog/aspnet-mvc-custom-error-pages