Filters in ASP.NET MVC CodePlex Preview 4

In Preview 2 or Preview 3 of ASP.NET (I forget which), we introduced the concept of Action Filters. Sounds much more exciting than your run-of-the-mill LayOnTheCouchMunchingChipsWatchingInfomercialsFilter, that I originally proposed to the team. Thankfully, that was rejected.

An action filter is an attribute you can slap on an action method in order to run some code before and after the action method executes. Typically, an action filter represents a cross-cutting concern to your action method. Output caching is a good example of a cross-cutting concern.

In CodePlex Preview 4 of ASP.NET MVC, we split out our action filters into four types of filters, each of which is an interface.

  • IAuthorizationFilter
  • IActionFilter
  • IResultFilter
  • IExceptionFilter

IAuthorizationFilter

Authorization filters run before any of the action filters and allow you to cancel the action. If you cancel the action, you can set the ActionResult instance you want rendered in response to the current request.

There should be very few cases (hopefully) that you need to write such a filter of your own. In those rare cases when you do, you’ll be glad to have this interface around.

IActionFilter

Action filters allow you to run code before and after an action method is called, but before the result of the action method is executed. This effectively allows you to hook into the rendering of the view, for example.

In the “before” method (OnActionExecuting), you can cancel the action and even supply an action result of your own instead. If you cancel the action, no other filters higher up the stack will be executed and the invoker starts executing the “after” method for any action filter that had its “before” method called (except for the filter that canceled the action).

In the after method (OnActionExecuted) you can’t cancel the action (it already ran and we don’t have a ITimeMachineFilter implemented yet), but you can replace or modify the action result before it gets called.

If an exception was thrown by another action filter or by the action method itself, you can examine the exception thrown from your filter. Your filter can specify that it can handle the exception (seriously, only do this if your filter really can do this as it’s generally a bad thing to handle an exception you shouldn’t be handling), in which case the action result will still get executed. If the exception propagates up, the result will not get executed.

IResultFilter

Result filters are pretty much similar to action filters, except they run after the action method has executed, but before the result returned from the action method has been executed. The “before” method is called OnResultExecuting and the “after” method is called OnResultExecuted.

IExceptionFilter

The exception filters are all guaranteed to run after all of the action filters and result filters have run. Even if an exception filter indicates that it can handle the exception, it will still run. This is useful for logging scenarios in cases where you want a filter to always run no matter what happens so it can log exceptions etc…

One interesting thing to note is that exception filters run after result filters. So what can you do from an exception filter? Well we give you one last ditch chance to render something to the user by allowing you to set the action result in the exception filter. If that action result throws an exception, you’re SOL and the exception filter does not handle that exception. Well, you’re not totally SOL. The normal ASP.NET web.config settings for custom errors will kick in if you set them.

Writing Custom Filters

To write a custom filter, you simply need to create an attribute (aka a class that inherits from FilterAttribute) that also implements one of the four interfaces I mentioned.

It turns out that we think the most common case for custom filters will be those that implement IActionFilter and/or IResultFilter. To support the common case, we included a base attribute ActionFilterAttribute, which inherits both of these interfaces. Yeah, the name isn’t exactly accurate, but we tend to think of action filters as really action and action result filters.

For the other two filter types, we did not include a base attribute type for these. To write your own authorization filter, you simply implement IAuthorizationFilter. For example, here’s a filter I wrote the other day which we will probably include in the MvcFutures assembly. Apply this filter to an action and it will perform request validation of potentially insecure input. (Side Note: This validation is on by default in ASP.NET WebForm applications, but not in ASP.NET MVC applications because it’s implemented by the Page class, which runs too late.)

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, 
  Inherited = true, AllowMultiple = false)]
public sealed class ValidateInputAttribute : FilterAttribute
    , IAuthorizationFilter {
  public void OnAuthorization(AuthorizationContext filterContext) {
    filterContext.HttpContext.Request.ValidateInput();
  }
}

While we did not include a base attribute for these filters, we did include concrete implementations of these interfaces. For example, the AuthorizeAttribute is a concrete implementation of an authorization filter. You can (er…will be able to) inherit from this attribute if you want, but you can also simply implement IAuthorizationFilter yourself.

Why Four Filter Types?

We debated this a long time. We could have stuck with just the two interfaces: IActionFilter and IResultFilter and handled all cases.

The problem we ran into is that for attributes that perform some sort of authentication check, you want to be absolutely sure it runs before any of the action filters. And it’s very easy to get this wrong by accident even if you know what you are doing.

The type of thing we wanted to avoid was accidentally running the output cache filter before the the authorization filter. That’s a recipe for an information disclosure bug, potentially displaying information to someone who shouldn’t have access to see it such as photos of your hair piece collection (Why do you have so man?). So we decided that there ought to be four distinct filter phases in the life of a controller action: Authorization, Action Execution, Result Execution, Exception Handling.

If you write an authorization filter, it is guaranteed to run before any other action filters.

Keep in mind though, that these phases merely help guide filter writers into doing the right thing. Because the MVC framework is all about leaving you in control, it is still possible to get it all wrong. For example, I could write a custom output caching filter that implements IAuthorizationFilter and thus runs at the wrong time. Please don’t do this. Code responsibly.

What others have said

Requesting Gravatar... Steve Sanderson Aug 14, 2008 7:36 AM
# re: Filters in ASP.NET MVC CodePlex Preview 4
Filters in Preview 4 are pretty sweet. Nice design work!
Requesting Gravatar... Derik Whittaker Aug 14, 2008 8:16 AM
# re: Filters in ASP.NET MVC CodePlex Preview 4
I love the fact that they all have interfaces. I am also looking forward to the next drop when the based classes are not sealed :)

Requesting Gravatar... Israel Aece Aug 14, 2008 8:22 AM
# re: Filters in ASP.NET MVC CodePlex Preview 4
Hello Phil,

Are there plans for execute an action (method) in async mode? (like ASP.NET 2.0 Async Pages)
Requesting Gravatar... tgmdbm Aug 14, 2008 9:22 AM
# re: Filters in ASP.NET MVC CodePlex Preview 4
Agreed, Filters in Preview 4 are awesome.

Paragraph 1 of Writing Custom Filters needs to change. You need to inherit from FilterAttribute, not just Attribute.
Requesting Gravatar... Kevin Miller Aug 14, 2008 10:21 AM
# re: Filters in ASP.NET MVC CodePlex Preview 4
For the record as long as we are talking about attributes Is there
any plan to support setting your own FilterFactory like what was done with controllers? Something like:

FilterBuilder.Current.SetFilterFactory(new StructureMapFilterFactory());

I would love it if there was support for doing Dependency Injection with filters. I have a custom filter used for security and testing the filter is a bit trickier than a normal DI scenario. With no IoC container creating the filter I have to do service location to retrieve dependencies from the container. To get around this I have do something like this:

public override void OnActionExecuting(ActionExecutingContext filterContext) {
//TODO - ICK Service location get rid of this. Use IoC.
var authenticationService = ObjectFactory.GetInstance();
var redirectService = ObjectFactory.GetInstance();

RedirectToLoginIfNotAuthenticated(authenticationService, redirectService);
}

...

Note: I will look to see if it makes sense to changing this to implement IAuthorizationFilter.
Requesting Gravatar... Erik Aug 14, 2008 10:55 AM
# re: Filters in ASP.NET MVC CodePlex Preview 4
[LayOnTheCouchMunchingChipsWatchingInfomercialsFilter()]
public void DoErikStuff(){
Erik.DoActiveStuff();
}


So that explains it...
Requesting Gravatar... haacked Aug 14, 2008 12:07 PM
# re: Filters in ASP.NET MVC CodePlex Preview 4
@tgmdbm that's not true. You don't have to implement FilterAttribute. You can simply inherit from Attribute and one of the filter interfaces in CodePlex Preview 4.
Requesting Gravatar... haacked Aug 14, 2008 12:08 PM
# re: Filters in ASP.NET MVC CodePlex Preview 4
@Kevin Miller - we don't have current plans for this in 1.0, but we do for 2.0. However, I'll see if we can get a quick prototype up in MvcFutures as I think it's possible with a little bit of extra work already. We just don't have an easy mechanism for doing it.
Requesting Gravatar... Steve Sanderson Aug 14, 2008 12:53 PM
# re: Filters in ASP.NET MVC CodePlex Preview 4
> "You don't have to implement FilterAttribute. You can simply inherit from Attribute and one of the filter interfaces in CodePlex Preview 4"

@haacked, are you sure? I thought that at first, too (www.codeplex.com/.../View.aspx?WorkItemId=2044) but Levi corrected me. Looking at the source for ControllerActionInvoker (in GetFiltersForActionMethod()) suggests that it only picks up attributes that derive from FilterAttribute.
Requesting Gravatar... haacked Aug 14, 2008 7:39 PM
# re: Filters in ASP.NET MVC CodePlex Preview 4
@tgmdbm and @SteveSanderson. Aye yi yi. You both are absolutely correct. Man, I can't seem to keep track of all these changes we make in my head! :) Sorry about that. I'll correct the post. :)
Requesting Gravatar... Lorenzo Melato Aug 15, 2008 3:55 AM
# re: Filters in ASP.NET MVC CodePlex Preview 4
Ciao Phil,

I'm also interested for the question that Israel Aece have posted:

Are there plans for execute an action (method) in async mode? (like ASP.NET 2.0 Async Pages)

I have post the same question on an early article of ScottGu about MVC but he never have post an answer.
Requesting Gravatar... haacked Aug 15, 2008 8:42 AM
# re: Filters in ASP.NET MVC CodePlex Preview 4
We don't currently have plans for that in v1.0, though it is still open for debate. We may try to include prototypes of this in MVC Futures and then get it ready for Post RTM.
Requesting Gravatar... Joe Chung Aug 17, 2008 1:22 AM
# re: Filters in ASP.NET MVC CodePlex Preview 4
I was looking at the AuthorizeAttribute implementation in Preview 4, which looked good, but I didn't see a DeauthorizeAttribute implementation which would enable developers to deny access to ASP.NET MVC content based on their credentials, analogous to deny elements in the Web.config system.web/authorization element. Maybe there's something like that post-Preview 4; I haven't checked yet.

Anyways, keep up the great work! I'm looking forward to ASP.NET MVC RTW! :)
Requesting Gravatar... Dmitriy Nagirnyak Aug 19, 2008 11:50 PM
# re: Filters in ASP.NET MVC CodePlex Preview 4
How is it possible to override attributes - from child parent classes to child classes and actions?
[Authenticate]
public AppController : Controller {
}

[Authenticate(Required=false)]
public HomeController : AppController {
}

There's no guaranteed order of executing these filters.
Requesting Gravatar... haacked Aug 21, 2008 8:28 AM
# re: Filters in ASP.NET MVC CodePlex Preview 4
@Dmitry you can set the Order property of the attributes.

What do you have to say?

(will show your gravatar)
Please add 7 and 6 and type the answer here: