The standard [HandleError] action filter which comes with ASP NET MVC has a very useful feature beside its widely known standard use, which is:
[HandleError]
public class MyController : Controller
Indeed, you can associate various kind of exceptions each to its own view, this gives you to your users friendly errors (a prerequisite for every post web 1.0 application):
//More specifics exceptions come first
[HandleError(ExceptionType = typeof(ResourceNotFoundException), ViewName="Error404", Order = 1)]
[HandleError(Order = 2)]
public class MyController : Controller
That’s all great BUT you can’t set the http status code you want to return to the browser, it will always return a generic 500 server error. Your users really don’t care much about status code being returned but search engines really do and they will be grateful if you behave as a good web citizen and use it appropriately.
What if you want to return a 404 to indicate that a resource is missing?
Enter SmarterHandleError filter, which is a filter identical to [HandleError] except that you can also set up the http status code being returned:
[SmartHandleError(ExceptionType=typeof(ResourceNotFoundException), ViewName="Error404", StatusCode=404, Order=1)] --> More specific exceptions come first
[SmartHandleError(ExceptionType=typeof(Exception), ViewName="Error", StatusCode=500, Order=2)]
public class MyController : Controller
That was my first iteration. The second one transformed SmartHandleError in an abstract class from which I derive a concrete class for every type of exception I need to handle:
[HandleError404(Order = 1)]
[HandleError500(Order = 2)]
public class MyController : Controller
Moreover SmartHandleError has a template method which you can override to provide some kind of different workflow depending on the catched exception type.
Here it is:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public abstract class SmartHandleErrorAttribute : FilterAttribute, IExceptionFilter
{
Type exceptionType = typeof(Exception);
int statusCode = 500;
string viewName = "Error";
public SmartHandleErrorAttribute(Type exceptionType, int statusCode, string viewName)
{
this.exceptionType = exceptionType;
this.statusCode = statusCode;
this.viewName = viewName;
}
public virtual void OnException(ExceptionContext filterContext)
{
Ex.Assert<ArgumentNullException>(filterContext.IsNotNull(), "filterContext");
if (filterContext.ExceptionHandled) return;
if (!filterContext.HttpContext.IsCustomErrorEnabled) return;
if (filterContext.Exception.IsNull()) return;
var exception = filterContext.Exception;
if (exception is TargetInvocationException)
exception = (exception as TargetInvocationException).InnerException;
if (!exceptionType.IsInstanceOfType(exception)) return; //it's not our exception
filterContext.Result = new ViewResult
{
ViewData = filterContext.Controller.ViewData,
TempData = filterContext.Controller.TempData,
ViewName = viewName,
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = statusCode;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
//Template method, override this in inherited class to execute custom logic
//for this exception
OnExceptionHandled(filterContext);
}
//Override this to add post-processing specific to the exception thrown
protected virtual void OnExceptionHandled(ExceptionContext filterContext)
{}
}
Thanks, this information help me a lot.