<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SquaredRoot &#187; action filters</title>
	<atom:link href="http://www.squaredroot.com/tag/action-filters/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.squaredroot.com</link>
	<description>.Net Development in DC</description>
	<lastBuildDate>Sun, 16 Aug 2009 01:30:15 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>MVC: Action Filter for Handling Errors</title>
		<link>http://www.squaredroot.com/2008/04/02/mvc-error-handler-filter/</link>
		<comments>http://www.squaredroot.com/2008/04/02/mvc-error-handler-filter/#comments</comments>
		<pubDate>Wed, 02 Apr 2008 04:29:00 +0000</pubDate>
		<dc:creator>Troy Goode</dc:creator>
				<category><![CDATA[MVC]]></category>
		<category><![CDATA[action filters]]></category>
		<category><![CDATA[error handling]]></category>
		<category><![CDATA[exceptions]]></category>

		<guid isPermaLink="false">/post/2008/04/02/MVC-Error-Handler-Filter.aspx</guid>
		<description><![CDATA[A few months ago I posted an article and some code that contained filters for forms authentication and error handling for the Preview 1 (CTP) release of the MVC framework. Unfortunately the Preview 2 release that was made available a few weeks ago changed enough that the code I posted no longer works. What the [...]]]></description>
			<content:encoded><![CDATA[<p>
A few months ago I posted <a href="/post/2008/01/MVC-Authentication-and-Errors.aspx">an article and some code</a> that contained filters for forms authentication and error handling for the Preview 1 (CTP) release of the MVC framework. Unfortunately <a href="http://blogs.msdn.com/brada/archive/2008/03/05/asp-net-mvc-preview-2.aspx">the Preview 2 release</a> that was made available a few weeks ago changed enough that the code I posted no longer works.
</p>
<p>
What the Preview 2 release did provide, however, was a new built-in filter framework. Rob Conery has already gone through the trouble of <a href="http://blog.wekeroad.com/blog/aspnet-mvc-securing-your-controller-actions/">creating authentication filters</a> that cover most of the functionality I had before, but I have yet to see an implementation of a filter for error handling that I like. I&#39;ve gone ahead and started from scratch, throwing away my old filters, and created a new filter that I think covers most of the same scenarios as my old ErrorHandler filters while being much simpler to implement and use. Hopefully you&#39;ll find it useful.
</p>
<p>
First let&#39;s take a look at a simple use case scenario:
</p>
<div class="csharpcode-wrapper">
<div class="csharpcode">
<pre class="alt">
<span class="lnum">   1:</span> <span class="kwrd">public</span> <span class="kwrd">void</span> Product( <span class="kwrd">int</span>? id )
</pre>
<pre class="alteven">
<span class="lnum">   2:</span> {
</pre>
<pre class="alt">
<span class="lnum">   3:</span>     <span class="kwrd">if</span>( id == <span class="kwrd">null</span> )
</pre>
<pre class="alteven">
<span class="lnum">   4:</span>         <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">&quot;No Product ID&quot;</span> );
</pre>
<pre class="alt">
<span class="lnum">   5:</span>     RenderView( <span class="str">&quot;DisplayProduct&quot;</span>, GetProduct(id.Value) );
</pre>
<pre class="alteven">
<span class="lnum">   6:</span> }
</pre>
</div>
</div>
<p>
In the code above, we have a simple action that displays a product based upon the ID specified. What do we do when no ID is specified though? The &quot;correct&quot; thing to do seems to be to throw an exception, as we&#39;ve done, but now the user will see either (a) an ugly 500 error screen [worst case] or (b) be redirected to the generic error page [best case]. Sometimes we&#39;d like a bit more control than that though&#8230;
</p>
<p>
Let&#39;s go ahead and add our error handling filter to this action and tell it that whenever <strong>ArgumentNullException</strong> is thrown, redirect to the &quot;Products&quot; page, where the user can select a product with a valid ID.
</p>
<div class="csharpcode-wrapper">
<div class="csharpcode">
<pre class="alt">
<span class="lnum">   1:</span> [RedirectToUrlOnError(Type=<span class="kwrd">typeof</span>(ArgumentNullException),Url=<span class="str">&quot;/Products&quot;</span>)]
</pre>
<pre class="alteven">
<span class="lnum">   2:</span> <span class="kwrd">public</span> <span class="kwrd">void</span> Product( <span class="kwrd">int</span>? id )
</pre>
<pre class="alt">
<span class="lnum">   3:</span> {
</pre>
<pre class="alteven">
<span class="lnum">   4:</span>     <span class="kwrd">if</span>( id == <span class="kwrd">null</span> )
</pre>
<pre class="alt">
<span class="lnum">   5:</span>         <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">&quot;No Product ID&quot;</span> );
</pre>
<pre class="alteven">
<span class="lnum">   6:</span>     RenderView( <span class="str">&quot;DisplayProduct&quot;</span>, GetProduct(id.Value) );
</pre>
<pre class="alt">
<span class="lnum">   7:</span> }
</pre>
</div>
</div>
<p>
So we&#39;ve added a [<strong>RedirectToUrlOnError</strong>] attribute and supplied it with a <strong>Type</strong> property &#8211; detailing the exception to catch &#8211; and a <strong>Url</strong> property &#8211; specifying the <strong>Url</strong> to navigate to upon a matched exception. You&#39;ll notice we are making a call to the <strong>GetProduct</strong>(<em>int</em>) method to retrieve the product&#39;s model so that we can pass it into the view&#39;s ViewData. What if this method were to fail? What if we weren&#39;t entirely certain what exception it would throw, or maybe we didn&#39;t care, we just want to handle any exception except for <strong>ArgumentNullException</strong> (which is already being handled). In this case we&#39;ll add another filter, but this time we will not specify the <strong>Type</strong> of exception that it should catch and just tell it that if anything isn&#39;t caught by another error handler redirect to the homepage.
</p>
<div class="csharpcode-wrapper">
<div class="csharpcode">
<pre class="alt">
<span class="lnum">   1:</span> [RedirectToUrlOnError(Type=<span class="kwrd">typeof</span>(ArgumentNullException),Url=<span class="str">&quot;/Products&quot;</span>)]
</pre>
<pre class="alteven">
<span class="lnum">   2:</span> [RedirectToUrlOnError(Url=<span class="str">&quot;/&quot;</span>)]
</pre>
<pre class="alt">
<span class="lnum">   3:</span> <span class="kwrd">public</span> <span class="kwrd">void</span> Product( <span class="kwrd">int</span>? id )
</pre>
<pre class="alteven">
<span class="lnum">   4:</span> {
</pre>
<pre class="alt">
<span class="lnum">   5:</span>     <span class="kwrd">if</span>( id == <span class="kwrd">null</span> )
</pre>
<pre class="alteven">
<span class="lnum">   6:</span>         <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">&quot;No Product ID&quot;</span> );
</pre>
<pre class="alt">
<span class="lnum">   7:</span>     RenderView( <span class="str">&quot;DisplayProduct&quot;</span>, GetProduct(id.Value) );
</pre>
<pre class="alteven">
<span class="lnum">   8:</span> }
</pre>
</div>
</div>
<p>
You can have as many error handler filters attached to an action as you need, but only one may have no Type specified.
</p>
<p>
Now let&#39;s take a look at the code for the filter itself:
</p>
<div class="csharpcode-wrapper">
<div class="csharpcode">
<pre class="alt">
<span class="lnum">   1:</span> <span class="kwrd">using</span> System;
</pre>
<pre class="alteven">
<span class="lnum">   2:</span> <span class="kwrd">using</span> System.Collections.Generic;
</pre>
<pre class="alt">
<span class="lnum">   3:</span> <span class="kwrd">using</span> System.Linq;
</pre>
<pre class="alteven">
<span class="lnum">   4:</span> <span class="kwrd">using</span> System.Web.Mvc;
</pre>
<pre class="alt">
<span class="lnum">   5:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">   6:</span> <span class="kwrd">namespace</span> SquaredRoot.Mvc.Filters.ErrorHandling
</pre>
<pre class="alt">
<span class="lnum">   7:</span> {
</pre>
<pre class="alteven">
<span class="lnum">   8:</span>     <span class="kwrd">public</span> <span class="kwrd">class</span> RedirectToUrlOnErrorAttribute : RedirectOnErrorAttribute
</pre>
<pre class="alt">
<span class="lnum">   9:</span>     {
</pre>
<pre class="alteven">
<span class="lnum">  10:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  11:</span>         <span class="kwrd">public</span> <span class="kwrd">string</span> Url{ get; set; }
</pre>
<pre class="alteven">
<span class="lnum">  12:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  13:</span>         <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">bool</span> Validate( FilterExecutedContext filterContext )
</pre>
<pre class="alteven">
<span class="lnum">  14:</span>         {
</pre>
<pre class="alt">
<span class="lnum">  15:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  16:</span>             <span class="rem">//### the url property is always needed</span>
</pre>
<pre class="alt">
<span class="lnum">  17:</span>             <span class="kwrd">if</span>( <span class="kwrd">string</span>.IsNullOrEmpty( Url ) || Url.Trim() == <span class="kwrd">string</span>.Empty )
</pre>
<pre class="alteven">
<span class="lnum">  18:</span>                 <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">&quot;RedirectToUrlOnErrorAttribute&#39;s Url property must have a value.&quot;</span> );
</pre>
<pre class="alt">
<span class="lnum">  19:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  20:</span>             <span class="rem">//### continue execution</span>
</pre>
<pre class="alt">
<span class="lnum">  21:</span>             <span class="kwrd">return</span> <span class="kwrd">true</span>;
</pre>
<pre class="alteven">
<span class="lnum">  22:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  23:</span>         }
</pre>
<pre class="alteven">
<span class="lnum">  24:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  25:</span>         <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Redirect( FilterExecutedContext filterContext )
</pre>
<pre class="alteven">
<span class="lnum">  26:</span>         {
</pre>
<pre class="alt">
<span class="lnum">  27:</span>             filterContext.ExceptionHandled = <span class="kwrd">true</span>;
</pre>
<pre class="alteven">
<span class="lnum">  28:</span>             filterContext.HttpContext.Response.Redirect( Url, <span class="kwrd">true</span> );
</pre>
<pre class="alt">
<span class="lnum">  29:</span>         }
</pre>
<pre class="alteven">
<span class="lnum">  30:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  31:</span>     }
</pre>
<pre class="alteven">
<span class="lnum">  32:</span> }
</pre>
</div>
</div>
<p>
So the [<strong>RedirectToUrlOnError</strong>] attribute inherits from the [<strong>RedirectOnError</strong>] attribute, which is where most of the hard work is done. We&#39;ll take a look at that base class in a bit, but first let&#39;s look at the other attribute you can use to trap and respond to errors &#8211; the [<strong>RedirectToActionOnError</strong>] attribute. We&#39;ll continue with the <strong>Product</strong>(<em>id</em>) sample from above, but this time redirect to an action rather than a hardcoded Url:
</p>
<div class="csharpcode-wrapper">
<div class="csharpcode">
<pre class="alt">
<span class="lnum">   1:</span> [RedirectToActionOnError(
</pre>
<pre class="alteven">
<span class="lnum">   2:</span>     Type=<span class="kwrd">typeof</span>(ArgumentNullException),
</pre>
<pre class="alt">
<span class="lnum">   3:</span>     Controller=<span class="kwrd">typeof</span>(ProductController),
</pre>
<pre class="alteven">
<span class="lnum">   4:</span>     Action=<span class="str">&quot;Index&quot;</span> )]
</pre>
<pre class="alt">
<span class="lnum">   5:</span> [RedirectToUrlOnError(Url=<span class="str">&quot;/&quot;</span>)]
</pre>
<pre class="alteven">
<span class="lnum">   6:</span> <span class="kwrd">public</span> <span class="kwrd">void</span> Product( <span class="kwrd">int</span>? id )
</pre>
<pre class="alt">
<span class="lnum">   7:</span> {
</pre>
<pre class="alteven">
<span class="lnum">   8:</span>     <span class="kwrd">if</span>( id == <span class="kwrd">null</span> )
</pre>
<pre class="alt">
<span class="lnum">   9:</span>         <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">&quot;No Product ID&quot;</span> );
</pre>
<pre class="alteven">
<span class="lnum">  10:</span>     RenderView( <span class="str">&quot;DisplayProduct&quot;</span>, GetProduct(id.Value) );
</pre>
<pre class="alt">
<span class="lnum">  11:</span> }
</pre>
</div>
</div>
<p>
You can see that this time instead of providing the Url property we are using providing the type of the controller that contains our target action, and the name of the action as a string. (Unfortunately lambda expressions are not allowed as parameters to an attribute, so I was limited in my options here. If you have a better idea, please let me know!) Also note that the catch-all is still there as a [<strong>RedirectToUrlOnError</strong>] attribute. You may use the [<strong>RedirectToActionOnError</strong>] attribute as a catch-all and you can mix and match the two attribute types, but still only one catch-all attribute total is allowed per action (in other words, you cannot have one of each).
</p>
<p>
Now let&#39;s see the code for this filter:
</p>
<div class="csharpcode-wrapper">
<div class="csharpcode">
<pre class="alt">
<span class="lnum">   1:</span> <span class="kwrd">using</span> System;
</pre>
<pre class="alteven">
<span class="lnum">   2:</span> <span class="kwrd">using</span> System.Collections.Generic;
</pre>
<pre class="alt">
<span class="lnum">   3:</span> <span class="kwrd">using</span> System.Linq;
</pre>
<pre class="alteven">
<span class="lnum">   4:</span> <span class="kwrd">using</span> System.Web.Mvc;
</pre>
<pre class="alt">
<span class="lnum">   5:</span> <span class="kwrd">using</span> System.Web.Routing;
</pre>
<pre class="alteven">
<span class="lnum">   6:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">   7:</span> <span class="kwrd">namespace</span> SquaredRoot.Mvc.Filters.ErrorHandling
</pre>
<pre class="alteven">
<span class="lnum">   8:</span> {
</pre>
<pre class="alt">
<span class="lnum">   9:</span>     <span class="kwrd">public</span> <span class="kwrd">class</span> RedirectToActionOnErrorAttribute : RedirectOnErrorAttribute
</pre>
<pre class="alteven">
<span class="lnum">  10:</span>     {
</pre>
<pre class="alt">
<span class="lnum">  11:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  12:</span>         <span class="kwrd">public</span> Type Controller{ get; set; }
</pre>
<pre class="alt">
<span class="lnum">  13:</span>         <span class="kwrd">public</span> <span class="kwrd">string</span> Action{ get; set; }
</pre>
<pre class="alteven">
<span class="lnum">  14:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  15:</span>         <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">bool</span> Validate( FilterExecutedContext filterContext )
</pre>
<pre class="alteven">
<span class="lnum">  16:</span>         {
</pre>
<pre class="alt">
<span class="lnum">  17:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  18:</span>             <span class="rem">//### the url property is always needed</span>
</pre>
<pre class="alt">
<span class="lnum">  19:</span>             <span class="kwrd">if</span>(
</pre>
<pre class="alteven">
<span class="lnum">  20:</span>                 Controller == <span class="kwrd">null</span> ||
</pre>
<pre class="alt">
<span class="lnum">  21:</span>                 ( <span class="kwrd">string</span>.IsNullOrEmpty( Action ) || Action.Trim() == <span class="kwrd">string</span>.Empty )
</pre>
<pre class="alteven">
<span class="lnum">  22:</span>             )
</pre>
<pre class="alt">
<span class="lnum">  23:</span>                 <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException( <span class="str">&quot;RedirectToUrlOnActionAttribute&#39;s Controller and Action properties must have values.&quot;</span> );
</pre>
<pre class="alteven">
<span class="lnum">  24:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  25:</span>             <span class="rem">//### make sure the Contoller property is a Controller</span>
</pre>
<pre class="alteven">
<span class="lnum">  26:</span>             <span class="kwrd">if</span>( !<span class="kwrd">typeof</span>(System.Web.Mvc.Controller).IsAssignableFrom( Controller ) )
</pre>
<pre class="alt">
<span class="lnum">  27:</span>                 <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException( <span class="str">&quot;RedirectToUrlOnActionAttribute&#39;s Controller property&#39;s value must derive from System.Web.Mvc.Controller.&quot;</span> );
</pre>
<pre class="alteven">
<span class="lnum">  28:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  29:</span>             <span class="rem">//### continue processing</span>
</pre>
<pre class="alteven">
<span class="lnum">  30:</span>             <span class="kwrd">return</span> <span class="kwrd">true</span>;
</pre>
<pre class="alt">
<span class="lnum">  31:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  32:</span>         }
</pre>
<pre class="alt">
<span class="lnum">  33:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  34:</span>         <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Redirect( FilterExecutedContext filterContext )
</pre>
<pre class="alt">
<span class="lnum">  35:</span>         {
</pre>
<pre class="alteven">
<span class="lnum">  36:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  37:</span>             <span class="rem">//### turn &quot;Foo.Foo.Foo.BarController&quot; into &quot;Bar&quot;</span>
</pre>
<pre class="alteven">
<span class="lnum">  38:</span>             <span class="kwrd">string</span> controllerName = Controller.ToString();
</pre>
<pre class="alt">
<span class="lnum">  39:</span>             controllerName = controllerName.Substring( controllerName.LastIndexOf(<span class="str">&quot;.&quot;</span>) + 1 );
</pre>
<pre class="alteven">
<span class="lnum">  40:</span>             controllerName = controllerName.Substring( 0, controllerName.LastIndexOf(<span class="str">&quot;Controller&quot;</span>) );
</pre>
<pre class="alt">
<span class="lnum">  41:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  42:</span>             <span class="rem">//### turn route data into url</span>
</pre>
<pre class="alt">
<span class="lnum">  43:</span>             RouteValueDictionary rvd = <span class="kwrd">new</span> RouteValueDictionary( <span class="kwrd">new</span>{
</pre>
<pre class="alteven">
<span class="lnum">  44:</span>                 controller = controllerName,
</pre>
<pre class="alt">
<span class="lnum">  45:</span>                 action = Action
</pre>
<pre class="alteven">
<span class="lnum">  46:</span>             } );
</pre>
<pre class="alt">
<span class="lnum">  47:</span>             ControllerContext ctx = <span class="kwrd">new</span> ControllerContext(
</pre>
<pre class="alteven">
<span class="lnum">  48:</span>                 filterContext.HttpContext,
</pre>
<pre class="alt">
<span class="lnum">  49:</span>                 filterContext.RouteData,
</pre>
<pre class="alteven">
<span class="lnum">  50:</span>                 filterContext.Controller
</pre>
<pre class="alt">
<span class="lnum">  51:</span>             );
</pre>
<pre class="alteven">
<span class="lnum">  52:</span>             VirtualPathData vpd = RouteTable.Routes.GetVirtualPath( ctx, rvd );
</pre>
<pre class="alt">
<span class="lnum">  53:</span>             <span class="kwrd">string</span> url = vpd.VirtualPath;
</pre>
<pre class="alteven">
<span class="lnum">  54:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  55:</span>             <span class="rem">//### redirect</span>
</pre>
<pre class="alteven">
<span class="lnum">  56:</span>             filterContext.ExceptionHandled = <span class="kwrd">true</span>;
</pre>
<pre class="alt">
<span class="lnum">  57:</span>             filterContext.HttpContext.Response.Redirect( url, <span class="kwrd">true</span> );
</pre>
<pre class="alteven">
<span class="lnum">  58:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  59:</span>         }
</pre>
<pre class="alteven">
<span class="lnum">  60:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  61:</span>     }
</pre>
<pre class="alteven">
<span class="lnum">  62:</span> }
</pre>
</div>
</div>
<p>
Other than some complexity with determining the Url, everything is very similar to the other filter. Again it appears the base class is doing the heavy lifting. Let&#39;s finally take a look at that base class:
</p>
<div class="csharpcode-wrapper">
<div class="csharpcode">
<pre class="alt">
<span class="lnum">   1:</span> <span class="kwrd">using</span> System;
</pre>
<pre class="alteven">
<span class="lnum">   2:</span> <span class="kwrd">using</span> System.Collections.Generic;
</pre>
<pre class="alt">
<span class="lnum">   3:</span> <span class="kwrd">using</span> System.Linq;
</pre>
<pre class="alteven">
<span class="lnum">   4:</span> <span class="kwrd">using</span> System.Web.Mvc;
</pre>
<pre class="alt">
<span class="lnum">   5:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">   6:</span> <span class="kwrd">namespace</span> SquaredRoot.Mvc.Filters.ErrorHandling
</pre>
<pre class="alt">
<span class="lnum">   7:</span> {
</pre>
<pre class="alteven">
<span class="lnum">   8:</span>     <span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> RedirectOnErrorAttribute : ActionFilterAttribute
</pre>
<pre class="alt">
<span class="lnum">   9:</span>     {
</pre>
<pre class="alteven">
<span class="lnum">  10:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  11:</span>         <span class="kwrd">public</span> Type Type { get; set; }
</pre>
<pre class="alteven">
<span class="lnum">  12:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  13:</span>         <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> OnActionExecuted( FilterExecutedContext filterContext )
</pre>
<pre class="alteven">
<span class="lnum">  14:</span>         {
</pre>
<pre class="alt">
<span class="lnum">  15:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  16:</span>             <span class="rem">//### check for errors</span>
</pre>
<pre class="alt">
<span class="lnum">  17:</span>             <span class="kwrd">if</span>( !Validate(filterContext) )
</pre>
<pre class="alteven">
<span class="lnum">  18:</span>                 <span class="kwrd">return</span>;
</pre>
<pre class="alt">
<span class="lnum">  19:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  20:</span>             <span class="rem">//### make sure the Type property is an Exception</span>
</pre>
<pre class="alt">
<span class="lnum">  21:</span>             <span class="kwrd">if</span>( Type != <span class="kwrd">null</span> &amp;&amp; !<span class="kwrd">typeof</span>(System.Exception).IsAssignableFrom( Type ) )
</pre>
<pre class="alteven">
<span class="lnum">  22:</span>                 <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException( <span class="str">&quot;RedirectOnErrorAttribute&#39;s Type property&#39;s value must derive from System.Exception.&quot;</span> );
</pre>
<pre class="alt">
<span class="lnum">  23:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  24:</span>             <span class="rem">//### if no exception occurred, stop processing this filter</span>
</pre>
<pre class="alt">
<span class="lnum">  25:</span>             <span class="kwrd">if</span>( filterContext.Exception == <span class="kwrd">null</span> )
</pre>
<pre class="alteven">
<span class="lnum">  26:</span>                 <span class="kwrd">return</span>;
</pre>
<pre class="alt">
<span class="lnum">  27:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  28:</span>             <span class="rem">//### get inner exception unless it is null (this should never happen?)</span>
</pre>
<pre class="alt">
<span class="lnum">  29:</span>             Exception ex = filterContext.Exception.InnerException ?? filterContext.Exception;
</pre>
<pre class="alteven">
<span class="lnum">  30:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  31:</span>             <span class="rem">//### if exception was thrown because of Response.Redirect, ignore it</span>
</pre>
<pre class="alteven">
<span class="lnum">  32:</span>             <span class="kwrd">if</span>( ex.GetType() == <span class="kwrd">typeof</span>(System.Threading.ThreadAbortException) )
</pre>
<pre class="alt">
<span class="lnum">  33:</span>                 <span class="kwrd">return</span>;
</pre>
<pre class="alteven">
<span class="lnum">  34:</span>             <span class="kwrd">else</span> <span class="kwrd">if</span>( Type == <span class="kwrd">typeof</span>(System.Threading.ThreadAbortException) )
</pre>
<pre class="alt">
<span class="lnum">  35:</span>                 <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException( <span class="str">&quot;Cannot catch exceptions of type &#39;ThreadAbortException&#39;.&quot;</span> );
</pre>
<pre class="alteven">
<span class="lnum">  36:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  37:</span>             <span class="rem">//### if the specified Type matches the thrown exception, process it</span>
</pre>
<pre class="alteven">
<span class="lnum">  38:</span>             <span class="kwrd">if</span>( IsExactMatch(ex) )
</pre>
<pre class="alt">
<span class="lnum">  39:</span>                 Redirect( filterContext );
</pre>
<pre class="alteven">
<span class="lnum">  40:</span>             <span class="rem">//### if this attribute has no specified Type, investigate further (this attribute is a catch-all error handler)</span>
</pre>
<pre class="alt">
<span class="lnum">  41:</span>             <span class="kwrd">else</span> <span class="kwrd">if</span>( Type == <span class="kwrd">null</span> )
</pre>
<pre class="alteven">
<span class="lnum">  42:</span>             {
</pre>
<pre class="alt">
<span class="lnum">  43:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  44:</span>                 <span class="rem">//### loop through all other RedirectToUrlOnErrorAttribute on this method</span>
</pre>
<pre class="alt">
<span class="lnum">  45:</span>                 <span class="kwrd">foreach</span>( RedirectOnErrorAttribute att <span class="kwrd">in</span> GetAllAttributes( filterContext ) )
</pre>
<pre class="alteven">
<span class="lnum">  46:</span>                     <span class="rem">//### ignore self</span>
</pre>
<pre class="alt">
<span class="lnum">  47:</span>                     <span class="kwrd">if</span>( att.GetHashCode() == <span class="kwrd">this</span>.GetHashCode() )
</pre>
<pre class="alteven">
<span class="lnum">  48:</span>                         <span class="kwrd">continue</span>;
</pre>
<pre class="alt">
<span class="lnum">  49:</span>                     <span class="rem">//### if another catch-all attribute is found, throw an exception</span>
</pre>
<pre class="alteven">
<span class="lnum">  50:</span>                     <span class="kwrd">else</span> <span class="kwrd">if</span>( att.Type == <span class="kwrd">null</span> )
</pre>
<pre class="alt">
<span class="lnum">  51:</span>                         <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException( <span class="str">&quot;Only one RedirectOnErrorAttribute per Action may be specified without its Type property provided.&quot;</span> );
</pre>
<pre class="alteven">
<span class="lnum">  52:</span>                     <span class="rem">//### if an exact match is found, stop processing the catch-all. that attribute has priority</span>
</pre>
<pre class="alt">
<span class="lnum">  53:</span>                     <span class="kwrd">else</span> <span class="kwrd">if</span>( att.IsExactMatch(ex) )
</pre>
<pre class="alteven">
<span class="lnum">  54:</span>                         <span class="kwrd">return</span>;
</pre>
<pre class="alt">
<span class="lnum">  55:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  56:</span>                 <span class="rem">//### no exact matches were found. if the specified Type for the catch-all fits, process here</span>
</pre>
<pre class="alt">
<span class="lnum">  57:</span>                 Redirect(filterContext);
</pre>
<pre class="alteven">
<span class="lnum">  58:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  59:</span>             }
</pre>
<pre class="alteven">
<span class="lnum">  60:</span>             <span class="kwrd">else</span>
</pre>
<pre class="alt">
<span class="lnum">  61:</span>                 <span class="rem">//### specified Type was not null, but did not match the thrown exception. don&#39;t process</span>
</pre>
<pre class="alteven">
<span class="lnum">  62:</span>                 <span class="kwrd">return</span>;
</pre>
<pre class="alt">
<span class="lnum">  63:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  64:</span>         }
</pre>
<pre class="alt">
<span class="lnum">  65:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  66:</span>         <span class="kwrd">public</span> <span class="kwrd">bool</span> IsExactMatch( Exception exception )
</pre>
<pre class="alt">
<span class="lnum">  67:</span>         {
</pre>
<pre class="alteven">
<span class="lnum">  68:</span>             <span class="kwrd">if</span>( Type != <span class="kwrd">null</span> &amp;&amp; exception.GetType() == Type )
</pre>
<pre class="alt">
<span class="lnum">  69:</span>                 <span class="kwrd">return</span> <span class="kwrd">true</span>;
</pre>
<pre class="alteven">
<span class="lnum">  70:</span>             <span class="kwrd">else</span>
</pre>
<pre class="alt">
<span class="lnum">  71:</span>                 <span class="kwrd">return</span> <span class="kwrd">false</span>;
</pre>
<pre class="alteven">
<span class="lnum">  72:</span>         }
</pre>
<pre class="alt">
<span class="lnum">  73:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  74:</span>         <span class="kwrd">private</span> List&lt;RedirectOnErrorAttribute&gt; GetAllAttributes( FilterExecutedContext filterContext )
</pre>
<pre class="alt">
<span class="lnum">  75:</span>         {
</pre>
<pre class="alteven">
<span class="lnum">  76:</span>             <span class="kwrd">return</span> filterContext.ActionMethod
</pre>
<pre class="alt">
<span class="lnum">  77:</span>                 .GetCustomAttributes( <span class="kwrd">typeof</span>( RedirectOnErrorAttribute ), <span class="kwrd">false</span> )
</pre>
<pre class="alteven">
<span class="lnum">  78:</span>                 .Select( a =&gt; a <span class="kwrd">as</span> RedirectOnErrorAttribute )
</pre>
<pre class="alt">
<span class="lnum">  79:</span>                 .ToList();
</pre>
<pre class="alteven">
<span class="lnum">  80:</span>         }
</pre>
<pre class="alt">
<span class="lnum">  81:</span>&nbsp;
</pre>
<pre class="alteven">
<span class="lnum">  82:</span>         <span class="kwrd">protected</span> <span class="kwrd">abstract</span> <span class="kwrd">bool</span> Validate( FilterExecutedContext filterContext );
</pre>
<pre class="alt">
<span class="lnum">  83:</span>         <span class="kwrd">protected</span> <span class="kwrd">abstract</span> <span class="kwrd">void</span> Redirect( FilterExecutedContext filterContext );
</pre>
<pre class="alteven">
<span class="lnum">  84:</span>&nbsp;
</pre>
<pre class="alt">
<span class="lnum">  85:</span>     }
</pre>
<pre class="alteven">
<span class="lnum">  86:</span> }
</pre>
</div>
</div>
<p>
That&#39;s all there is. Feel free to take it, use it, change it, whatever. I tried my best to document it thoroughly with comments, but if you have any questions just drop a comment below and I&#39;ll try to respond quickly. I do ask that if you make any improvements, please leave a comment here letting everyone know what you&#39;ve changed so that we can all benefit.
</p>
<p>
<strong>Here are the filters in a downloadable format:<br />
</strong>
</p>
<p>
<strong><a rel="enclosure" href="/file.axd?file=RedirectOnErrorAttributes.zip">RedirectOnErrorAttributes.zip (2.63 kb)</a></strong></p>
<div style="text-align: center;"><a href="http://www.dotnetkicks.com/kick/?url=http://www.squaredroot.com/2008/04/02/mvc-error-handler-filter/" style="border:0; position: relative; top: -2px;"><img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http://www.squaredroot.com/2008/04/02/mvc-error-handler-filter/" style="border:0;" alt="Kick It on DotNetKicks.com" /></a><a href="http://dotnetshoutout.com/Submit?url=http://www.squaredroot.com/2008/04/02/mvc-error-handler-filter/" style="border: 0;"><img src="http://dotnetshoutout.com/image.axd?url=http://www.squaredroot.com/2008/04/02/mvc-error-handler-filter/" style="border:0px" alt="Shout It on DotNetShoutOuts.com" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.squaredroot.com/2008/04/02/mvc-error-handler-filter/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>MVC Authentication and Errors</title>
		<link>http://www.squaredroot.com/2008/01/04/mvc-authentication-and-errors/</link>
		<comments>http://www.squaredroot.com/2008/01/04/mvc-authentication-and-errors/#comments</comments>
		<pubDate>Fri, 04 Jan 2008 05:25:00 +0000</pubDate>
		<dc:creator>Troy Goode</dc:creator>
				<category><![CDATA[MVC]]></category>
		<category><![CDATA[action filters]]></category>
		<category><![CDATA[authentication]]></category>
		<category><![CDATA[error handling]]></category>
		<category><![CDATA[exceptions]]></category>

		<guid isPermaLink="false">/post/2008/01/04/MVC-Authentication-and-Errors.aspx</guid>
		<description><![CDATA[NOTE: This article was written for the December CTP release of the MVC framework. Unfortunately, it does not entirely apply to the Preview 2 release or subsequent releases. I love working with the recent CTP release of the ASP.Net MVC framework, but it is definitely an early release and is lacking many of the developer [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p><strong>NOTE</strong>:<br />
This article was written for the December CTP release of the MVC framework. Unfortunately, it does not entirely apply to the Preview 2 release or subsequent releases.</p></blockquote>
<p>I love working with the <a href="http://weblogs.asp.net/scottgu/archive/2007/12/09/asp-net-3-5-extensions-ctp-preview-released.aspx">recent CTP release</a> of the ASP.Net MVC framework, but it is definitely an early release and is lacking many of the developer friendly features that we have grown to rely upon in WebForms. One such feature is WebForm&#8217;s easy to understand authentication model.</p>
<p>In the WebForms world URLs can be referenced in a web.config file and then have authentication rules applied to them. A rule that says that you must be logged in to view a secure page may look something like:</p>
<p><!--<br />
{rtf1ansiansicpglang1024noproof65001uc1 deff0{fonttbl{f0fnilfcharset0fprq1 Consolas;}}{colortbl;??red0green0blue255;red255green255blue255;red163green21blue21;red255green0blue0;red0green0blue0;}??fs18 cf1 &lt;cf3 locationcf1  cf4 pathcf1 =cf0 "cf1 loginRequired.aspxcf0 "cf1 &gt;par ??tab &lt;cf3 system.webcf1 &gt;par ??tab tab &lt;cf3 authorizationcf1 &gt;par ??tab tab tab &lt;cf3 denycf1  cf4 userscf1 =cf0 "cf1 ?cf0 "cf1  /&gt;par ??tab tab &lt;/cf3 authorizationcf1 &gt;par ??tab &lt;/cf3 system.webcf1 &gt;par ??&lt;/cf3 locationcf1 &gt;}<br />
--></p>
<div style="background: white none repeat scroll 0% 0%; font-size: 9pt; color: black; font-family: courier new; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p style="margin: 0px"><span style="color: #2b91af"> 1</span> <span style="color: blue">&lt;</span><span style="color: #a31515">location</span><span style="color: blue"> </span><span style="color: red">path</span><span style="color: blue">=</span>&#8220;<span style="color: blue">loginRequired.aspx</span>&#8220;<span style="color: blue">&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 2</span> <span style="color: blue"> &lt;</span><span style="color: #a31515">system.web</span><span style="color: blue">&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 3</span> <span style="color: blue"> &lt;</span><span style="color: #a31515">authorization</span><span style="color: blue">&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 4</span> <span style="color: blue"> &lt;</span><span style="color: #a31515">deny</span><span style="color: blue"> </span><span style="color: red">users</span><span style="color: blue">=</span>&#8220;<span style="color: blue">?</span>&#8220;<span style="color: blue"> /&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 5</span> <span style="color: blue"> &lt;/</span><span style="color: #a31515">authorization</span><span style="color: blue">&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 6</span> <span style="color: blue"> &lt;/</span><span style="color: #a31515">system.web</span><span style="color: blue">&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 7</span> <span style="color: blue">&lt;/</span><span style="color: #a31515">location</span><span style="color: blue">&gt;</span></p>
</div>
<p>A rule that says only users in the Administrators group may view it might look like this:</p>
<p><!--<br />
{rtf1ansiansicpglang1024noproof65001uc1 deff0{fonttbl{f0fnilfcharset0fprq1 Consolas;}}{colortbl;??red0green0blue255;red255green255blue255;red163green21blue21;red255green0blue0;red0green0blue0;}??fs18 cf1 &lt;cf3 locationcf1  cf4 pathcf1 =cf0 "cf1 adminRequired.aspxcf0 "cf1 &gt;par ??tab &lt;cf3 system.webcf1 &gt;par ??tab tab &lt;cf3 authorizationcf1 &gt;par ??tab tab tab &lt;cf3 allowcf1  cf4 rolescf1 =cf0 "cf1 Administratorscf0 "cf1  /&gt;par ??tab tab tab &lt;cf3 denycf1  cf4 userscf1 =cf0 "cf1 *cf0 "cf1  /&gt;par ??tab tab &lt;/cf3 authorizationcf1 &gt;par ??tab &lt;/cf3 system.webcf1 &gt;par ??&lt;/cf3 locationcf1 &gt;}<br />
--></p>
<div style="background: white none repeat scroll 0% 0%; font-size: 9pt; color: black; font-family: courier new; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p style="margin: 0px"><span style="color: #2b91af"> 1</span> <span style="color: blue">&lt;</span><span style="color: #a31515">location</span><span style="color: blue"> </span><span style="color: red">path</span><span style="color: blue">=</span>&#8220;<span style="color: blue">adminRequired.aspx</span>&#8220;<span style="color: blue">&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 2</span> <span style="color: blue"> &lt;</span><span style="color: #a31515">system.web</span><span style="color: blue">&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 3</span> <span style="color: blue"> &lt;</span><span style="color: #a31515">authorization</span><span style="color: blue">&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 4</span> <span style="color: blue"> &lt;</span><span style="color: #a31515">allow</span><span style="color: blue"> </span><span style="color: red">roles</span><span style="color: blue">=</span>&#8220;<span style="color: blue">Administrators</span>&#8220;<span style="color: blue"> /&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 5</span> <span style="color: blue"> &lt;</span><span style="color: #a31515">deny</span><span style="color: blue"> </span><span style="color: red">users</span><span style="color: blue">=</span>&#8220;<span style="color: blue">*</span>&#8220;<span style="color: blue"> /&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 6</span> <span style="color: blue"> &lt;/</span><span style="color: #a31515">authorization</span><span style="color: blue">&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 7</span> <span style="color: blue"> &lt;/</span><span style="color: #a31515">system.web</span><span style="color: blue">&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 8</span> <span style="color: blue">&lt;/</span><span style="color: #a31515">location</span><span style="color: blue">&gt;</span></p>
</div>
<p>It didn&#8217;t take long for me to run into the lack of any central authentication scheme in the new MVC framework. I searched around and found some <a href="http://weblogs.asp.net/fredriknormen/archive/2007/11/25/asp-net-mvc-framework-security.aspx">older information from prior to the CTP release posted by Fredrik Normén</a> that seemed to address my issues, but unfortunately one of the features his solution requires did not make its way into the CTP release: attribute based exception handling.</p>
<p>Looking through the code samples on the page you see how he uses the .Net frameworks built in <a href="http://msdn2.microsoft.com/en-us/library/system.security.permissions.principalpermission.aspx"><strong>PrincipalPermission</strong></a> attribute (from <strong>System.Security.Permissions</strong>) to classify an action as demanding the user be in a specific role. If the user is not in that role the .Net framework will throw a <strong>SecurityException</strong>. What good does that do? Well take a look at line 3 in the below code:</p>
<p><!--<br />
{rtf1ansiansicpglang1024noproof65001uc1 deff0{fonttbl{f0fnilfcharset0fprq1 Consolas;}}{colortbl;??red0green0blue0;red255green255blue255;red43green145blue175;red163green21blue21;red0green0blue255;}??fs18 [cf3 ControllerActioncf0 ]par ??[cf3 PrincipalPermissioncf0 (cf3 SecurityActioncf0 .Demand, Role=cf4 "Admin"cf0 ]par ??[cf3 ExceptionHandlercf0 (cf4 "Error"cf0 , cf5 typeofcf0 (SecurityException))]par ??cf5 publiccf0  cf5 voidcf0  Edit(cf5 intcf0 ? id)par ??{par ??   ...par ??}}<br />
--></p>
<div style="background: white none repeat scroll 0% 0%; font-size: 9pt; color: black; font-family: courier new; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p style="margin: 0px"><span style="color: #2b91af"> 1</span> [<span style="color: #2b91af">ControllerAction</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 2</span> [<span style="color: #2b91af">PrincipalPermission</span>(<span style="color: #2b91af">SecurityAction</span>.Demand, Role=<span style="color: #a31515">"Admin"</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 3</span> [<span style="color: #2b91af">ExceptionHandler</span>(<span style="color: #a31515">"Error"</span>, <span style="color: blue">typeof</span>(SecurityException))]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 4</span> <span style="color: blue">public</span> <span style="color: blue">void</span> Edit(<span style="color: blue">int</span>? id)</p>
<p style="margin: 0px"><span style="color: #2b91af"> 5</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 6</span> &#8230;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 7</span> }</p>
</div>
<p>The <strong>ExceptionHandler</strong> attribute appears to take two values:</p>
<ol>
<li>The view to render in the event of an error.</li>
<li>The type of error to match against.</li>
</ol>
<p>So based upon this code the <strong>PrincipalPermission</strong> will interrogate the user&#8217;s roles when the action is requested and if the user is not in the &#8220;Admin&#8221; role it will throw a <strong>SecurityException</strong>. At that point the <strong>ExceptionHandler</strong> will wake up and say &#8220;hey I can handle that&#8221; and render the view named &#8220;Error&#8221;. Neat huh? Too bad <strong>ExceptionHandler</strong> doesn&#8217;t exist&#8230;</p>
<p>Personally I liked most of the concepts that were introduced in Frederik&#8217;s post, so I went ahead and began to implement the <strong>ExceptionHandler</strong> attribute. Along the way I realized that what was really needed was a way to apply filters to a controller. I&#8217;ve seen <a href="http://flanders.co.nz/blog/archive/2007/12/17/implementing-filters-in-asp.net-mvc.aspx">Ivan Carrero&#8217;s controller filter implementation</a>, but I wanted filters that hooked straight into the MVC Controller&#8217;s three major lifecycle events: <strong>OnPreAction</strong>, <strong>OnPostAction</strong>, and <strong>OnError</strong>. By doing so I felt I would minimize the difference between code in a filter and code in a controller. Thus was born the <strong>FilterController</strong>.</p>
<h2>Filter Controller</h2>
<p>The <strong>FilterController</strong> is an abstract class deriving from the <strong>System.Web.Mvc.Controller</strong>. It&#8217;s primary purpose is to interrogate itself via reflection when it is created and to then load any attributes that implement the <strong>IControllerFilter</strong> interface:</p>
<p><!--<br />
{rtf1ansiansicpglang1024noproof65001uc1 deff0{fonttbl{f0fnilfcharset0fprq1 Consolas;}}{colortbl;??red0green0blue0;red255green255blue255;red128green128blue128;red0green128blue0;red0green0blue255;red43green145blue175;}??fs18 tab cf3 ///cf4  cf3 &lt;summary&gt;par ??cf0 tab cf3 ///cf4  Descendent of the MVC Controller class that adds capability of processing filters specified by attributes.par ??cf0 tab cf3 ///cf4  cf3 &lt;/summary&gt;par ??cf0 tab cf5 publiccf0  cf5 abstractcf0  cf5 classcf0  cf6 FilterControllercf0  : cf6 Controllerpar ??cf0 tab {par ??par ??tab tab cf3 ///cf4  cf3 &lt;summary&gt;par ??cf0 tab tab cf3 ///cf4  Default constructor.par ??cf0 tab tab cf3 ///cf4  cf3 &lt;/summary&gt;par ??cf0 tab tab cf5 publiccf0  FilterController()par ??tab tab {par ??tab tab tab Filters = GetFilterAttributes();par ??tab tab tab cf5 foreachcf0 ( cf6 IControllerFiltercf0  filter cf5 incf0  Filters )par ??tab tab tab {par ??tab tab tab tab filter.Initialize(cf5 thiscf0 );par ??tab tab tab }par ??tab tab }par ??}<br />
--></p>
<div style="background: white none repeat scroll 0% 0%; font-size: 9pt; color: black; font-family: courier new; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p style="margin: 0px"><span style="color: #2b91af"> 1</span> <span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;summary&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 2</span> <span style="color: gray">///</span><span style="color: green"> Descendent of the MVC Controller class that adds capability of processing filters specified by attributes.</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 3</span> <span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;/summary&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 4</span> <span style="color: blue">public</span> <span style="color: blue">abstract</span> <span style="color: blue">class</span> <span style="color: #2b91af">FilterController</span> : <span style="color: #2b91af">Controller</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 5</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 6</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 7</span> <span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;summary&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 8</span> <span style="color: gray">///</span><span style="color: green"> Default constructor.</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 9</span> <span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;/summary&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 10</span> <span style="color: blue">public</span> FilterController()</p>
<p style="margin: 0px"><span style="color: #2b91af"> 11</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 12</span> Filters = GetFilterAttributes();</p>
<p style="margin: 0px"><span style="color: #2b91af"> 13</span> <span style="color: blue">foreach</span>( <span style="color: #2b91af">IControllerFilter</span> filter <span style="color: blue">in</span> Filters )</p>
<p style="margin: 0px"><span style="color: #2b91af"> 14</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 15</span> filter.Initialize(<span style="color: blue">this</span>);</p>
<p style="margin: 0px"><span style="color: #2b91af"> 16</span> }</p>
<p style="margin: 0px"><span style="color: #2b91af"> 17</span> }</p>
</div>
<p>The filters are then called for each of the three integration events: <strong>OnPreAction</strong>, <strong>OnPostAction</strong>, and <strong>OnError</strong>. Here is what the <strong>OnError</strong> event does:</p>
<p><!--<br />
{rtf1ansiansicpglang1024noproof65001uc1 deff0{fonttbl{f0fnilfcharset0fprq1 Consolas;}}{colortbl;??red0green0blue0;red255green255blue255;red128green128blue128;red0green128blue0;red0green0blue255;red43green145blue175;}??fs18 tab tab cf3 ///cf4  cf3 &lt;summary&gt;par ??cf0 tab tab cf3 ///cf4  Passes control of the OnError event on to all filters that want to handle it.par ??cf0 tab tab cf3 ///cf4  cf3 &lt;/summary&gt;par ??cf0 tab tab cf3 ///cf4  cf3 &lt;param name="actionName"&gt;cf4 The name of the action being requested when the exception was thrown.cf3 &lt;/param&gt;par ??cf0 tab tab cf3 ///cf4  cf3 &lt;param name="methodInfo"&gt;cf4 Reflection object representing the action being requested when the exception was thrown.cf3 &lt;/param&gt;par ??cf0 tab tab cf3 ///cf4  cf3 &lt;param name="exception"&gt;cf4 The exception thrown while the action was being requested.cf3 &lt;/param&gt;par ??cf0 tab tab cf3 ///cf4  cf3 &lt;returns&gt;cf4 A boolean denoting the successful handling of the event.cf3 &lt;/returns&gt;par ??cf0 tab tab cf5 protectedcf0  cf5 overridecf0  cf5 boolcf0  OnError( cf5 stringcf0  actionName, cf6 MethodInfocf0  methodInfo, cf6 Exceptioncf0  exception )par ??tab tab {par ??tab tab tab cf5 boolcf0  handled = cf5 falsecf0 ;par ??tab tab tab cf5 boolcf0  allTrue = cf5 truecf0 ;par ??tab tab tab cf5 foreachcf0 ( cf6 IControllerFiltercf0  filter cf5 incf0  Filters )par ??tab tab tab {par ??tab tab tab tab cf5 ifcf0 ( filter.HandleOnError )par ??tab tab tab tab {par ??tab tab tab tab tab handled = cf5 truecf0 ;par ??tab tab tab tab tab cf5 ifcf0 ( !filter.OnError( actionName, methodInfo, exception ) )par ??tab tab tab tab tab tab allTrue = cf5 falsecf0 ;par ??tab tab tab tab }par ??tab tab tab }par ??tab tab tab cf5 ifcf0 ( !handled )par ??tab tab tab tab cf5 throwcf0  exception;par ??tab tab tab cf5 returncf0  allTrue;par ??tab tab }}<br />
--></p>
<div style="background: white none repeat scroll 0% 0%; font-size: 9pt; color: black; font-family: courier new; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p style="margin: 0px"><span style="color: #2b91af"> 1</span> <span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;summary&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 2</span> <span style="color: gray">///</span><span style="color: green"> Passes control of the OnError event on to all filters that want to handle it.</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 3</span> <span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;/summary&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 4</span> <span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;param name=&#8221;actionName&#8221;&gt;</span><span style="color: green">The name of the action being requested when the exception was thrown.</span><span style="color: gray">&lt;/param&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 5</span> <span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;param name=&#8221;methodInfo&#8221;&gt;</span><span style="color: green">Reflection object representing the action being requested when the exception was thrown.</span><span style="color: gray">&lt;/param&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 6</span> <span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;param name=&#8221;exception&#8221;&gt;</span><span style="color: green">The exception thrown while the action was being requested.</span><span style="color: gray">&lt;/param&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 7</span> <span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray">&lt;returns&gt;</span><span style="color: green">A boolean denoting the successful handling of the event.</span><span style="color: gray">&lt;/returns&gt;</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 8</span> <span style="color: blue">protected</span> <span style="color: blue">override</span> <span style="color: blue">bool</span> OnError( <span style="color: blue">string</span> actionName, <span style="color: #2b91af">MethodInfo</span> methodInfo, <span style="color: #2b91af">Exception</span> exception )</p>
<p style="margin: 0px"><span style="color: #2b91af"> 9</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 10</span> <span style="color: blue">bool</span> handled = <span style="color: blue">false</span>;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 11</span> <span style="color: blue">bool</span> allTrue = <span style="color: blue">true</span>;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 12</span> <span style="color: blue">foreach</span>( <span style="color: #2b91af">IControllerFilter</span> filter <span style="color: blue">in</span> Filters )</p>
<p style="margin: 0px"><span style="color: #2b91af"> 13</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 14</span> <span style="color: blue">if</span>( filter.HandleOnError )</p>
<p style="margin: 0px"><span style="color: #2b91af"> 15</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 16</span> handled = <span style="color: blue">true</span>;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 17</span> <span style="color: blue">if</span>( !filter.OnError( actionName, methodInfo, exception ) )</p>
<p style="margin: 0px"><span style="color: #2b91af"> 18</span> allTrue = <span style="color: blue">false</span>;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 19</span> }</p>
<p style="margin: 0px"><span style="color: #2b91af"> 20</span> }</p>
<p style="margin: 0px"><span style="color: #2b91af"> 21</span> <span style="color: blue">if</span>( !handled )</p>
<p style="margin: 0px"><span style="color: #2b91af"> 22</span> <span style="color: blue">throw</span> exception;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 23</span> <span style="color: blue">return</span> allTrue;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 24</span> }</p>
</div>
<p>The <strong>OnPreAction</strong> and <strong>OnPostAction</strong> events look almost exactly like the <strong>OnError</strong> event.</p>
<p>To fulfill my initial goal of obtaining functionality similar to that described in Frederik&#8217;s post, I have created two filters:</p>
<ul>
<li><strong>SecurityFilter</strong></li>
<li><strong>ErrorHandlerFilter</strong></li>
</ul>
<h2>Security Filter</h2>
<p>While the <strong>PrincipalPermission</strong> attribute used in Frederik&#8217;s post handles many security scenarios well, it wasn&#8217;t as flexible or keyboard friendly as I would prefer. I created the <strong>SecurityFilter</strong> and an arrangement of sub-filters to create what I think is an easier solution.</p>
<p>To use the security filter in your controller you must first inherit from <strong>FilterController</strong> and apply the <strong>[SecurityFilter]</strong> attribute.</p>
<p><!--<br />
{rtf1ansiansicpglang1024noproof65001uc1 deff0{fonttbl{f0fnilfcharset0fprq1 Consolas;}}{colortbl;??red0green0blue255;red255green255blue255;red0green0blue0;red43green145blue175;}??fs18 cf1 usingcf0  System;par ??cf1 usingcf0  System.Web.Mvc;par ??cf1 usingcf0  SquaredRoot.Mvc;par ??par ??cf1 namespacecf0  Examplepar ??{par ??par ??tab [cf4 SecurityFiltercf0 ]par ??tab cf1 publiccf0  cf1 classcf0  cf4 MyControllercf0  : cf4 FilterControllerpar ??cf0 tab {par ??tab tab ...par ??tab }par ??}<br />
--></p>
<div style="background: white none repeat scroll 0% 0%; font-size: 9pt; color: black; font-family: courier new; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p style="margin: 0px"><span style="color: #2b91af"> 1</span> <span style="color: blue">using</span> System;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 2</span> <span style="color: blue">using</span> System.Web.Mvc;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 3</span> <span style="color: blue">using</span> SquaredRoot.Mvc;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 4</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 5</span> <span style="color: blue">namespace</span> Example</p>
<p style="margin: 0px"><span style="color: #2b91af"> 6</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 7</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 8</span> [<span style="color: #2b91af">SecurityFilter</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 9</span> <span style="color: blue">public</span> <span style="color: blue">class</span> <span style="color: #2b91af">MyController</span> : <span style="color: #2b91af">FilterController</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 10</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 11</span> &#8230;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 12</span> }</p>
</div>
<p>This alone does nothing, but you are now able to add one or more of the <strong>SecurityFilter</strong>&#8216;s sub-filters to this controller or it&#8217;s actions. The sub-filters I have created are:</p>
<ul>
<li><strong>RequireLogin</strong><br />
Validates that the user is logged in.</li>
<li><strong>RequireAnonymous</strong><br />
Validates that the user is NOT logged in.</li>
<li><strong>RequireRole</strong><br />
Validates that the user is in the specified role.</li>
<li><strong>RequireAnyRole</strong><br />
Validates that the user is in at least one of the specified roles</li>
<li><strong>RequireEachRole</strong><br />
Validates that the user is in every one of the specified roles.</li>
</ul>
<p>Let&#8217;s imagine a controller for a simple bulletin board system. In order to post to this forum you must be logged in, if you want to delete a post you must be in either the &#8220;Administrators&#8221; role or the &#8220;Moderators&#8221; role, and if you want to undelete a post you must be in the &#8220;Administrators&#8221; group. That controller would look something like:</p>
<p><!--<br />
{rtf1ansiansicpglang1024noproof65001uc1 deff0{fonttbl{f0fnilfcharset0fprq1 Consolas;}}{colortbl;??red0green0blue0;red255green255blue255;red43green145blue175;red0green0blue255;red163green21blue21;}??fs18 tab [cf3 SecurityFiltercf0 ]par ??tab [cf3 RequireLogincf0 ]par ??tab cf4 publiccf0  cf4 classcf0  cf3 ForumControllercf0  : cf3 FilterControllerpar ??cf0 tab {par ??par ??tab tab [cf3 ControllerActioncf0 ]par ??tab tab cf4 publiccf0  cf4 voidcf0  Post( cf4 stringcf0  message ){ ... }par ??par ??tab tab [cf3 ControllerActioncf0 ,cf3 RequireAnyRolecf0 ( cf5 "Administrators"cf0 , cf5 "Moderators"cf0  )]par ??tab tab cf4 publiccf0  cf4 voidcf0  Delete( cf4 intcf0  id ){ ... }par ??par ??tab tab [cf3 ControllerActioncf0 ,cf3 RequireRolecf0 ( cf5 "Administrators"cf0  )]par ??tab tab cf4 publiccf0  cf4 voidcf0  Undelete( cf4 intcf0  id ){ ... }par ??par ??tab }}<br />
--></p>
<div style="background: white none repeat scroll 0% 0%; font-size: 9pt; color: black; font-family: courier new; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p style="margin: 0px"><span style="color: #2b91af"> 1</span> [<span style="color: #2b91af">SecurityFilter</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 2</span> [<span style="color: #2b91af">RequireLogin</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 3</span> <span style="color: blue">public</span> <span style="color: blue">class</span> <span style="color: #2b91af">ForumController</span> : <span style="color: #2b91af">FilterController</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 4</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 5</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 6</span> [<span style="color: #2b91af">ControllerAction</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 7</span> <span style="color: blue">public</span> <span style="color: blue">void</span> Post( <span style="color: blue">string</span> message ){ &#8230; }</p>
<p style="margin: 0px"><span style="color: #2b91af"> 8</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 9</span> [<span style="color: #2b91af">ControllerAction</span>,<span style="color: #2b91af">RequireAnyRole</span>( <span style="color: #a31515">"Administrators"</span>, <span style="color: #a31515">"Moderators"</span> )]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 10</span> <span style="color: blue">public</span> <span style="color: blue">void</span> Delete( <span style="color: blue">int</span> id ){ &#8230; }</p>
<p style="margin: 0px"><span style="color: #2b91af"> 11</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 12</span> [<span style="color: #2b91af">ControllerAction</span>,<span style="color: #2b91af">RequireRole</span>( <span style="color: #a31515">"Administrators"</span> )]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 13</span> <span style="color: blue">public</span> <span style="color: blue">void</span> Undelete( <span style="color: blue">int</span> id ){ &#8230; }</p>
<p style="margin: 0px"><span style="color: #2b91af"> 14</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 15</span> }</p>
</div>
<p>By applying the [<strong>RequireLogin</strong>] attribute to the class you have applied that filter to all of the actions as well, which means you must be logged in to call the <strong>Post</strong> method. The other two methods use the appropriate version of the role requirement filters to achieve their goal.</p>
<p>What happens if the filter validations fail? In the case of an anonymous user attempting to access a restricted resource an <strong>AnonymousAccessException</strong> (which derives from <strong>SecurityException</strong>) is thrown while all other scenarios throw a <strong>SecurityException</strong>. What you do with those exceptions leads us to&#8230;</p>
<h2>Error Handler Filter</h2>
<p>Using the above <strong>ForumController</strong>, let&#8217;s add the <strong>ErrorHandler</strong> filter:</p>
<p><!--<br />
{rtf1ansiansicpglang1024noproof65001uc1 deff0{fonttbl{f0fnilfcharset0fprq1 Consolas;}}{colortbl;??red0green0blue0;red255green255blue255;red43green145blue175;red0green0blue255;}??fs18 tab [cf3 SecurityFiltercf0 ]par ??tab [cf3 ErrorHandlerFiltercf0 ]par ??tab [cf3 RequireLogincf0 ]par ??tab cf4 publiccf0  cf4 classcf0  cf3 ForumControllercf0  : cf3 FilterControllerpar ??cf0 tab {par ??tab tab ...par ??tab }}<br />
--></p>
<div style="background: white none repeat scroll 0% 0%; font-size: 9pt; color: black; font-family: courier new; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p style="margin: 0px"><span style="color: #2b91af"> 1</span> [<span style="color: #2b91af">SecurityFilter</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 2</span> [<span style="color: #2b91af">ErrorHandlerFilter</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 3</span> [<span style="color: #2b91af">RequireLogin</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 4</span> <span style="color: blue">public</span> <span style="color: blue">class</span> <span style="color: #2b91af">ForumController</span> : <span style="color: #2b91af">FilterController</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 5</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 6</span> &#8230;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 7</span> }</p>
</div>
<p>Like with the last filter, this filter by itself does nothing but allow us to use the <strong>ErrorHandler</strong> sub-filter. Let&#8217;s go ahead and add two sub-filters: one to handle security exceptions and one to handle all other exceptions.</p>
<p>In the event of a security exception we&#8217;ll render the &#8220;AccessDenied&#8221; view while all other exceptions will render the &#8220;SystemError&#8221; view:</p>
<p><!--<br />
{rtf1ansiansicpglang1024noproof65001uc1 deff0{fonttbl{f0fnilfcharset0fprq1 Consolas;}}{colortbl;??red0green0blue0;red255green255blue255;red43green145blue175;red255green0blue255;red163green21blue21;red0green0blue255;}??fs18 tab [cf3 SecurityFiltercf0 ]par ??tab [cf3 ErrorHandlerFiltercf0 ]par ??tab [cf3 RequireLogincf0 ]par ??tab [cf3 ErrorHandlercf0 ( cf4 1cf0 , cf5 "AccessDenied"cf0 , cf6 typeofcf0 (cf3 SecurityExceptioncf0 ) )]par ??tab [cf3 ErrorHandlercf0 ( cf4 2cf0 , cf5 "SystemError"cf0 , cf6 typeofcf0 (cf3 Exceptioncf0 ) )]par ??tab cf6 publiccf0  cf6 classcf0  cf3 ForumControllercf0  : cf3 FilterControllerpar ??cf0 tab {par ??tab tab ...par ??tab }}<br />
--></p>
<div style="background: white none repeat scroll 0% 0%; font-size: 9pt; color: black; font-family: courier new; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p style="margin: 0px"><span style="color: #2b91af"> 1</span> [<span style="color: #2b91af">SecurityFilter</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 2</span> [<span style="color: #2b91af">ErrorHandlerFilter(ErrorHandlerMode.Render)</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 3</span> [<span style="color: #2b91af">RequireLogin</span>]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 4</span> [<span style="color: #2b91af">ErrorHandler</span>( <span style="color: fuchsia">1</span>, <span style="color: #a31515">"AccessDenied"</span>, <span style="color: blue">typeof</span>(<span style="color: #2b91af">SecurityException</span>) )]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 5</span> [<span style="color: #2b91af">ErrorHandler</span>( <span style="color: fuchsia">2</span>, <span style="color: #a31515">"SystemError"</span>, <span style="color: blue">typeof</span>(<span style="color: #2b91af">Exception</span>) )]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 6</span> <span style="color: blue">public</span> <span style="color: blue">class</span> <span style="color: #2b91af">ForumController</span> : <span style="color: #2b91af">FilterController</span></p>
<p style="margin: 0px"><span style="color: #2b91af"> 7</span> {</p>
<p style="margin: 0px"><span style="color: #2b91af"> 8</span> &#8230;</p>
<p style="margin: 0px"><span style="color: #2b91af"> 9</span> }</p>
</div>
<p>First notice that we are now providing the <strong>ErrorHandlerFilter</strong> attribute with an option that says <strong>ErrorHandlerMode.Render</strong>. This is because in the event of an error we want the controller to render the view with the name passed in. Later on we&#8217;ll look at the other mode: <strong>ErrorHandlerMode.Redirect</strong>.</p>
<p>Next notice that we are providing three values to each of the two <strong>ErrorHandler</strong> sub-filters:</p>
<ol>
<li>The order in which the sub-filter should be processed. This is important because the order the attribute is returned by reflection is unknown.</li>
<li>The name of the view to render. Just like calling <strong>RenderView</strong>() from an action, this view name must be accessible to the controller (either in the controller&#8217;s view directory or in the <strong>Shared</strong> directory).</li>
<li>The type of the exception to match against.</li>
</ol>
<p>Keep in mind that these sub-filters could be applied at either the class level or the method level and that method-level sub-filters are processed before class-level sub-filters. We&#8217;ll stick with class-level throughout this article.</p>
<p>An &#8220;Access Denied&#8221; page popping up whenever we try to go somewhere we aren&#8217;t allowed to without logging in isn&#8217;t the best user experience. Let&#8217;s improve it by sending anonymous users to the login page instead. This time however, we don&#8217;t want just render the login view, we want to actually redirect to the <strong>SecurityController</strong>&#8216;s <strong>Login</strong> action. While we&#8217;re at it, I&#8217;ll show you an example of handling multiple exceptions with one sub-filter:</p>
<p><!--<br />
{rtf1ansiansicpglang1024noproof65001uc1 deff0{fonttbl{f0fnilfcharset0fprq1 Consolas;}}{colortbl;??red0green0blue0;red255green255blue255;red43green145blue175;red255green0blue255;red163green21blue21;red0green0blue255;}??fs18 tab [cf3 ErrorHandlercf0 ( cf4 1cf0 , cf5 "Login,Security"cf0 ,par ??tab cf3 ErrorHandlerModecf0 .Redirect,par ??tab tab cf6 typeofcf0 (cf3 AnonymousAccessExceptioncf0 ) )]par ??tab [cf3 ErrorHandlercf0 ( cf4 2cf0 , cf5 "AccessDenied"cf0 ,par ??tab tab cf6 typeofcf0 (cf3 SecurityExceptioncf0 ) )]par ??tab [cf3 ErrorHandlercf0 ( cf4 3cf0 , cf5 "BadArgument"cf0 ,par ??tab tab cf6 typeofcf0 (cf3 ArgumentExceptioncf0 ),par ??tab tab cf6 typeofcf0 (cf3 ArgumentNullExceptioncf0 ),par ??tab tab cf6 typeofcf0 (cf3 ArgumentOutOfRangeExceptioncf0 ) )]par ??tab [cf3 ErrorHandlercf0 ( cf4 4cf0 , cf5 "SystemError"cf0 ,par ??tab tab cf6 typeofcf0 (cf3 Exceptioncf0 ) )]}<br />
--></p>
<div style="background: white none repeat scroll 0% 0%; font-size: 9pt; color: black; font-family: courier new; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p style="margin: 0px"><span style="color: #2b91af"> 1</span> [<span style="color: #2b91af">ErrorHandler</span>( <span style="color: fuchsia">1</span>, <span style="color: #a31515">"Login,Security"</span>,</p>
<p style="margin: 0px"><span style="color: #2b91af"> 2</span> <span style="color: #2b91af">ErrorHandlerMode</span>.Redirect,</p>
<p style="margin: 0px"><span style="color: #2b91af"> 3</span> <span style="color: blue">typeof</span>(<span style="color: #2b91af">AnonymousAccessException</span>) )]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 4</span> [<span style="color: #2b91af">ErrorHandler</span>( <span style="color: fuchsia">2</span>, <span style="color: #a31515">"AccessDenied"</span>,</p>
<p style="margin: 0px"><span style="color: #2b91af"> 5</span> <span style="color: blue">typeof</span>(<span style="color: #2b91af">SecurityException</span>) )]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 6</span> [<span style="color: #2b91af">ErrorHandler</span>( <span style="color: fuchsia">3</span>, <span style="color: #a31515">"BadArgument"</span>,</p>
<p style="margin: 0px"><span style="color: #2b91af"> 7</span> <span style="color: blue">typeof</span>(<span style="color: #2b91af">ArgumentException</span>),</p>
<p style="margin: 0px"><span style="color: #2b91af"> 8</span> <span style="color: blue">typeof</span>(<span style="color: #2b91af">ArgumentNullException</span>),</p>
<p style="margin: 0px"><span style="color: #2b91af"> 9</span> <span style="color: blue">typeof</span>(<span style="color: #2b91af">ArgumentOutOfRangeException</span>) )]</p>
<p style="margin: 0px"><span style="color: #2b91af"> 10</span> [<span style="color: #2b91af">ErrorHandler</span>( <span style="color: fuchsia">4</span>, <span style="color: #a31515">"SystemError"</span>,</p>
<p style="margin: 0px"><span style="color: #2b91af"> 11</span> <span style="color: blue">typeof</span>(<span style="color: #2b91af">Exception</span>) )]</p>
</div>
<p>Here in line 1 we specify not the name of the view to render, but the name of the action and controller to redirect to (in the format &#8220;action,controller&#8221;). The handler knows to process this as a redirect because we&#8217;ve changed the mode for this one sub-filter to <strong>ErrorHandlerMode.Redirect</strong>. Lines 7, 8, &amp; 9 illustrate the capability for one sub-filter to match against many exceptions.</p>
<h2>Download The Code</h2>
<p>I hope you find these filters useful. If they don&#8217;t happen to match your particular problem, then feel free to write your own filters. To do so you only have to implement the <strong>IControllerFilter</strong> interface. I&#8217;ve attempted to make it even easier to do so by providing a base class named <strong>ControllerFilter</strong> that already implements the interface and has several hooks for you to take advantage of.</p>
<p>You are free to use or modify this code for anything including commercial purposes. The only restriction I ask is that you do not take credit for this work yourself, but I do not require any specific attribution.</p>
<p>I have packaged the code into four different releases:</p>
<p><strong>NOTE:</strong> <span style="color: red">These projects were built on Vista x64. If you are running a 32-bit version of Windows you may initially have trouble building the source versions below. To fix this you&#8217;ll need to re-add the System.Web.Extensions reference.</span> <a href="/post/2008/01/MVC-Authentication-and-Errors.aspx#Commentb18287b2-6e57-43be-88f0-022f0d247959">See my comment below for more details.</a></p>
<p><strong>Full Source Release<br />
</strong>The entire solution zipped up.<br />
<a rel="enclosure" href="/file.axd?file=MVC+Controller+Filters+(Source+and+Example+Website).zip">Controller Filters (Full Source).zip</a></p>
<p><strong>Example Site &amp; Binaries Release</strong><br />
<strong>(Recommended)</strong> Just the binaries zipped up with an example site.<br />
<a rel="enclosure" href="/file.axd?file=MVC+Controller+Filters+(Example+Website).zip">Controller Filters (Example Site).zip</a></p>
<p><strong>Filter Source Release</strong><br />
The source code for the FilterController and filters. No example site.<br />
<a rel="enclosure" href="/file.axd?file=MVC+Controller+Filters+(Source).zip">Controller Filters (Filter Source).zip</a><strong> </strong></p>
<p><strong>Filter Binaries Release</strong><br />
The binaries for the FilterController and filters only. No source.<br />
<a rel="enclosure" href="/file.axd?file=MVC+Controller+Filters+(Binaries).zip">Controller Filters (Filter Binaries).zip</a> </p>
<div style="text-align: center;"><a href="http://www.dotnetkicks.com/kick/?url=http://www.squaredroot.com/2008/01/04/mvc-authentication-and-errors/" style="border:0; position: relative; top: -2px;"><img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http://www.squaredroot.com/2008/01/04/mvc-authentication-and-errors/" style="border:0;" alt="Kick It on DotNetKicks.com" /></a><a href="http://dotnetshoutout.com/Submit?url=http://www.squaredroot.com/2008/01/04/mvc-authentication-and-errors/" style="border: 0;"><img src="http://dotnetshoutout.com/image.axd?url=http://www.squaredroot.com/2008/01/04/mvc-authentication-and-errors/" style="border:0px" alt="Shout It on DotNetShoutOuts.com" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.squaredroot.com/2008/01/04/mvc-authentication-and-errors/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
	</channel>
</rss>
