Created
November 3, 2012 05:36
-
-
Save troygoode/4006173 to your computer and use it in GitHub Desktop.
MVC: Action Filter for Handling Errors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void Product( int? id ) { | |
if( id == null ) | |
throw new ArgumentNullException( "No Product ID" ); | |
RenderView( "DisplayProduct", GetProduct(id.Value) ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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) ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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) ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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) ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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