Filters in ASP.NET MVC CodePlex Preview 4

aspnetmvc 0 comments suggest edit

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.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

15 responses

  1. Avatar for Steve Sanderson
    Steve Sanderson August 13th, 2008

    Filters in Preview 4 are pretty sweet. Nice design work!

  2. Avatar for Derik Whittaker
    Derik Whittaker August 13th, 2008

    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 :)

  3. Avatar for Israel Aece
    Israel Aece August 13th, 2008

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

  4. Avatar for tgmdbm
    tgmdbm August 13th, 2008

    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.

  5. Avatar for Kevin Miller
    Kevin Miller August 13th, 2008

    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<iauthenticationservice>();
    var redirectService = ObjectFactory.GetInstance<iredirectservice>();
    RedirectToLoginIfNotAuthenticated(authenticationService, redirectService);
    }
    ...
    Note: I will look to see if it makes sense to changing this to implement IAuthorizationFilter.

  6. Avatar for Erik
    Erik August 13th, 2008

    [LayOnTheCouchMunchingChipsWatchingInfomercialsFilter()]
    public void DoErikStuff(){
    Erik.DoActiveStuff();
    }

    So that explains it...

  7. Avatar for haacked
    haacked August 14th, 2008

    @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.

  8. Avatar for haacked
    haacked August 14th, 2008

    @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.

  9. Avatar for Steve Sanderson
    Steve Sanderson August 14th, 2008

    > "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.

  10. Avatar for haacked
    haacked August 14th, 2008

    @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. :)

  11. Avatar for Lorenzo Melato
    Lorenzo Melato August 14th, 2008

    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.

  12. Avatar for haacked
    haacked August 14th, 2008

    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.

  13. Avatar for Joe Chung
    Joe Chung August 16th, 2008

    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! :)

  14. Avatar for Dmitriy Nagirnyak
    Dmitriy Nagirnyak August 19th, 2008

    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.

  15. Avatar for haacked
    haacked August 20th, 2008

    @Dmitry you can set the Order property of the attributes.