Skip to content

Instantly share code, notes, and snippets.

@troygoode
Created November 3, 2012 05:36
Show Gist options
  • Save troygoode/4006173 to your computer and use it in GitHub Desktop.
Save troygoode/4006173 to your computer and use it in GitHub Desktop.
MVC: Action Filter for Handling Errors
public void Product( int? id ) {
if( id == null )
throw new ArgumentNullException( "No Product ID" );
RenderView( "DisplayProduct", GetProduct(id.Value) );
}
[RedirectToUrlOnError(Type=typeof(ArgumentNullException),Url="/Products")]
public void Product( int? id ) {
if( id == null )
throw new ArgumentNullException( "No Product ID" );
RenderView( "DisplayProduct", GetProduct(id.Value) );
}
[RedirectToUrlOnError(Type=typeof(ArgumentNullException),Url="/Products")]
[RedirectToUrlOnError(Url="/")]
public void Product( int? id ) {
if( id == null )
throw new ArgumentNullException( "No Product ID" );
RenderView( "DisplayProduct", GetProduct(id.Value) );
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
namespace SquaredRoot.Mvc.Filters.ErrorHandling {
public class RedirectToUrlOnErrorAttribute : RedirectOnErrorAttribute {
public string Url{ get; set; }
protected override bool Validate( FilterExecutedContext filterContext ) {
//### the url property is always needed
if( string.IsNullOrEmpty( Url ) || Url.Trim() == string.Empty )
throw new ArgumentNullException( "RedirectToUrlOnErrorAttribute's Url property must have a value." );
//### continue execution
return true;
}
protected override void Redirect( FilterExecutedContext filterContext ) {
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Redirect( Url, true );
}
}
}
[RedirectToActionOnError(
Type=typeof(ArgumentNullException),
Controller=typeof(ProductController),
Action="Index" )]
[RedirectToUrlOnError(Url="/")]
public void Product( int? id ) {
if( id == null )
throw new ArgumentNullException( "No Product ID" );
RenderView( "DisplayProduct", GetProduct(id.Value) );
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
namespace SquaredRoot.Mvc.Filters.ErrorHandling {
public class RedirectToActionOnErrorAttribute : RedirectOnErrorAttribute {
public Type Controller{ get; set; }
public string Action{ get; set; }
protected override bool Validate( FilterExecutedContext filterContext ) {
//### the url property is always needed
if(
Controller == null ||
( string.IsNullOrEmpty( Action ) || Action.Trim() == string.Empty )
)
throw new ArgumentNullException( "RedirectToUrlOnActionAttribute's Controller and Action properties must have values." );
//### make sure the Contoller property is a Controller
if( !typeof(System.Web.Mvc.Controller).IsAssignableFrom( Controller ) )
throw new ArgumentException( "RedirectToUrlOnActionAttribute's Controller property's value must derive from System.Web.Mvc.Controller." );
//### continue processing
return true;
}
protected override void Redirect( FilterExecutedContext filterContext ) {
//### turn "Foo.Foo.Foo.BarController" into "Bar"
string controllerName = Controller.ToString();
controllerName = controllerName.Substring( controllerName.LastIndexOf(".") + 1 );
controllerName = controllerName.Substring( 0, controllerName.LastIndexOf("Controller") );
//### turn route data into url
RouteValueDictionary rvd = new RouteValueDictionary( new{
controller = controllerName,
action = Action
} );
ControllerContext ctx = new ControllerContext(
filterContext.HttpContext,
filterContext.RouteData,
filterContext.Controller
);
VirtualPathData vpd = RouteTable.Routes.GetVirtualPath( ctx, rvd );
string url = vpd.VirtualPath;
//### redirect
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Redirect( url, true );
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
namespace SquaredRoot.Mvc.Filters.ErrorHandling {
public abstract class RedirectOnErrorAttribute : ActionFilterAttribute {
public Type Type { get; set; }
public override void OnActionExecuted( FilterExecutedContext filterContext ) {
//### check for errors
if( !Validate(filterContext) )
return;
//### make sure the Type property is an Exception
if( Type != null && !typeof(System.Exception).IsAssignableFrom( Type ) )
throw new ArgumentException( "RedirectOnErrorAttribute's Type property's value must derive from System.Exception." );
//### if no exception occurred, stop processing this filter
if( filterContext.Exception == null )
return;
//### get inner exception unless it is null (this should never happen?)
Exception ex = filterContext.Exception.InnerException ?? filterContext.Exception;
//### if exception was thrown because of Response.Redirect, ignore it
if( ex.GetType() == typeof(System.Threading.ThreadAbortException) )
return;
else if( Type == typeof(System.Threading.ThreadAbortException) )
throw new ArgumentException( "Cannot catch exceptions of type 'ThreadAbortException'." );
//### if the specified Type matches the thrown exception, process it
if( IsExactMatch(ex) )
Redirect( filterContext );
//### if this attribute has no specified Type, investigate further (this attribute is a catch-all error handler)
else if( Type == null ) {
//### loop through all other RedirectToUrlOnErrorAttribute on this method
foreach( RedirectOnErrorAttribute att in GetAllAttributes( filterContext ) )
//### ignore self
if( att.GetHashCode() == this.GetHashCode() )
continue;
//### if another catch-all attribute is found, throw an exception
else if( att.Type == null )
throw new ArgumentException( "Only one RedirectOnErrorAttribute per Action may be specified without its Type property provided." );
//### if an exact match is found, stop processing the catch-all. that attribute has priority
else if( att.IsExactMatch(ex) )
return;
//### no exact matches were found. if the specified Type for the catch-all fits, process here
Redirect(filterContext);
}else
//### specified Type was not null, but did not match the thrown exception. don't process
return;
}
public bool IsExactMatch( Exception exception ) {
if( Type != null && exception.GetType() == Type )
return true;
else
return false;
}
private List<RedirectOnErrorAttribute> GetAllAttributes( FilterExecutedContext filterContext ) {
return filterContext.ActionMethod
.GetCustomAttributes( typeof( RedirectOnErrorAttribute ), false )
.Select( a => a as RedirectOnErrorAttribute )
.ToList();
}
protected abstract bool Validate( FilterExecutedContext filterContext );
protected abstract void Redirect( FilterExecutedContext filterContext );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment