New Filter Overrides feature in ASP.NET MVC 5 and ASP.NET Web API 2
Introduction:
ASP.NET MVC 5 and ASP.NET Web API 2 added a very important feature called Filter Overrides. A good definition of filter overrides is available in release notes, "You can now override which filters apply to a given action method or controller, by specifying an override filter. Override filters specify a set of filter types that should not run for a given scope (action or controller). This allows you to add global filters, but then exclude some from specific actions or controllers". In this quick article, I will tell you the problem that filter overrides is trying to solve(with an example). I will also show you a quick fix of a bug that I found in ASP.NET MVC 5 regarding filter overrides.
Description:
Let say you have an Authorize filter applied on a controller and a action,
[Authorize(Roles = "PowerUsers")] public class AController : ApiController { // GET api/api public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } [Authorize(Roles = "Admin")] // GET api/api/5 public string Get(int id) { return "value"; } }
Now you want that all actions of above controller should only run when the user is in PowerUsers role except for second Get method which requires an Admin role. Before ASP.NET MVC 5 and ASP.NET Web API 2, it was not possible to achieve this(without using a dirty hack). But the current version make this a piece of cake, using OverrideAuthorizationAttribute. Just put this attribute on the second Get action above and then you are done. For the further understanding of this, just use this controller and custom authorize filter,
public class MyAuthorizeAttribute : AuthorizeAttribute { string name; public MyAuthorizeAttribute(string s) { name = s; } public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext) { base.OnAuthorization(actionContext); } } [MyAuthorize("Controller")] public class AController : ApiController { [MyAuthorize("Action")] //[OverrideAuthorization] // GET api/api public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } // GET api/api/5 public string Get(int id) { return "value"; } }
Now just put a break-point on MyAuthorizeAttribute constructor and OnAuthorization method. You will find that the constructor is invoked for both action and controller but only the controller's MyAuthorizeAttribute.OnAuthorization(by checking name variable) is called. Now just un-comment the [OverrideAuthorization], you will find that action method's MyAuthorizeAttribute.OnAuthorization is called. When you do the same in MVC 5, it will not work due to a bug(AFAIK) in MVC 5. It's fixed in MVC 5.1 . But for now you can use this hack to make this work in MVC 5 as well. Here is an example,
public class OverrideAuthorizeAttribute : AuthorizeAttribute, IOverrideFilter { public Type FiltersToOverride { get { return typeof(IAuthorizationFilter); } } } [Authorize(Users="Admin")] public class HomeController : Controller { [OverrideAuthorize(Users = "Imran Baloch")] public ActionResult Index() { return View(); } public ActionResult About() { ViewBag.Message = "Your application description page."; return View(); } public ActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } }
The hack is simply creating a new OverrideAuthorizeAttribute inheriting from AuthorizeAttribute and implementing IOverrideFilter interface and then applying this to the Index action method. Flip also written a cool blog at Overriding filters in ASP.NET Web API vNext.
Summary:
In this article, I showed you a new cool filter overrides feature in ASP.NET MVC 5 and ASP.NET Web API 2. I also showed you a work-around of a bug available only in MVC 5. Hopefully you will enjoy my this article too.