The Power Struggle of FilterAttribute

I’ve been doing a lot of MVC2 work lately, and have been indescribably thrilled with how easy it is to write clean code with it (or at least what I consider clean code). Being able to Unit Test my Controllers and have separation from everything else is like magic. OK, maybe I am a little late to this ballgame. I discovered a very cool feature of MVC2, and that is the FilterAttribute. When reading documentation about how to ensure controller actions could only be run if the user was Authenticated, I naturally came to the AuthorizeAttribute. It was that simple! I read the documentation, and to my delight it is extensible to make your own FilterAttributes. It becomes more powerful when you put the IAuthorizationFilter interface on your attribute, too. Now I can preemptively short circuit the use of an action.

I wanted an attribute that would allow me to say, “Hey, if you are already logged in, just go here instead.” It doesn’t make sense to show a Sign Up page if the user is already logged in, just take them Home. Here is what I ended up with:

   1: public class RedirectToActionIfAuthenticatedAttribute : FilterAttribute, IAuthorizationFilter

   2: {

   3:     private readonly string _controller;

   4:     private readonly string _action;


   6:     public RedirectToActionIfAuthenticatedAttribute(string actionName, string controllerName)

   7:     {

   8:         _controller = controllerName;

   9:         _action = actionName;

  10:     }


  12:     public void OnAuthorization(AuthorizationContext filterContext)

  13:     {

  14:         var authenticationService = IoC.Resolve<IAuthenticationService>();

  15:         if (authenticationService.GetCurrentUser().HasValue)

  16:         {

  17:             filterContext.Result = new RedirectToRouteResult

  18:                     (

  19:                         new RouteValueDictionary

  20:                             {

  21:                                 {"controller", _controller},

  22:                                 {"action", _action}

  23:                             });

  24:         }

  25:     }

  26: }

The implementation is easy: the OnAuthorization method comes from IAuthorizationFilter. It seems a bit odd to be using this for things that really aren’t purely authorization related, but the internal attributes in the MVC2 kit also use it this way, so I was a bit relaxed. At this point, filterContext has a property called Result. If you leave it null, the attribute has no affect. If you set it to something by the time OnAuthorization exits, then that will trump the execution of your controller action. In this case, I am assuming you are using the out-of-the-box default route and populating the controller and action.

It has a verbose name, but I tend to like these kind of names. Regardless, I can now throw this attribute on controller actions to redirect them wherever I want if they are already signed in. It’s usage is like so:

   1: [RedirectToActionIfAuthenticated("MyAccount", "Home")]

   2: public ActionResult SignUp()

   3: {

   4:     return View();

   5: }

If a user tries to view the SignUp view and they are already authenticated, then just take them to the MyAccount action, which in this case is a view, on the Home controller.

At this point, there was a slew of application I could think of for these kind of attributes. However…

Is it Metadata Abuse?

I was debating, and leaning towards no. One of the things that always came back to mind with this is that attributes are just metadata – or so I was taught originally. I can easily see this begin taken to extents that exceed their purpose. I only need this attribute twice (so far) in my code base. SignUp and LogOn views. It’s not a security things, more of a usability thing, so I am not worried about putting them on the HttpPost actions – just the HttpGet for now. Would it be more correct to just make the decision inside of the action itself? For some applications, like the AuthorizeAttribute – I can easily see the need. If you have a few dozen controller actions – and you probably do – duplicating the authentication logic that many times would be a big criticism. The other part is, I can test the attribute using my favorite testing tools, but testing the controller is now a bit different – do I test that the controller has the attribute? Can I test it without it being an integration test? A test that just asserts and attribute is present doesn’t give much meaning. I know the attribute is there – I can see it.

I’m still not 110% sure on my “style” of using MVC2 and a pattern that I can stick to. I really like that MVC2 has all of the extension points that I need. There is always a popular saying, “Just because you can, doesn’t mean you should”.

Leave a Reply

Your email address will not be published. Required fields are marked *