I've run into several cases in ASP.NET MVC where I wanted to apply an action filter on every action except one or two. For example, say you have an AccountController. Every action in it requires the user be logged in, so you add [Authorize] at the controller level. But say you want to include the login page in AccountController. The problem is, users sent to the login page aren't authorized, so this would result in an infinite loop.
The obvious fix (other than moving the Login action to another controller) is to move the [Authorize] from the controller to all action methods except Login. Well that ain't fun, especially when you have a lot of methods or forget to add [Authorize] to a new method.
Rails makes this easy with an ability to exclude filters. ASP.NET MVC doesn't let you. So I decided to make it possible and it was easier than I thought.
/// <summary> /// This will disable any filters of the given type from being applied. This is useful when, say, all but on action need the Authorize filter. /// </summary> [AttributeUsage(AttributeTargets.Method|AttributeTargets.Class, AllowMultiple=true)] public class ExcludeFilterAttribute : ActionFilterAttribute { public ExcludeFilterAttribute(Type toExclude) { FilterToExclude = toExclude; } /// <summary> /// The type of filter that will be ignored. /// </summary> public Type FilterToExclude { get; private set; } } /// <summary> /// A subclass of ControllerActionInvoker that implements the functionality of IgnoreFilterAttribute. To use this, just override Controller.CreateActionInvoker() and return an instance of this. /// </summary> public class ControllerActionInvokerWithExcludeFilter : ControllerActionInvoker { protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { //base implementation does all the hard work. we just prune off the filters to ignore var filterInfo = base.GetFilters(controllerContext, actionDescriptor); foreach( var toExclude in filterInfo.ActionFilters.OfType<ExcludeFilterAttribute>().Select(f=>f.FilterToExclude).ToArray() ) { RemoveWhere(filterInfo.ActionFilters, filter => toExclude.IsAssignableFrom(filter.GetType())); RemoveWhere(filterInfo.AuthorizationFilters, filter => toExclude.IsAssignableFrom(filter.GetType())); RemoveWhere(filterInfo.ExceptionFilters, filter => toExclude.IsAssignableFrom(filter.GetType())); RemoveWhere(filterInfo.ResultFilters, filter => toExclude.IsAssignableFrom(filter.GetType())); } return filterInfo; } /// <summary> /// Removes all elements from the list that satisfy the condition. Returns the list that was passed in (minus removed elements) for chaining. Ripped from one of my helper libraries (where it was a pretty extension method). /// </summary> private static IList<T> RemoveWhere<T>(IList<T> list, Predicate<T> predicate) { if (list == null || list.Count == 0) return list; //note: didn't use foreach because an exception will be thrown when you remove items during enumeration for (var i = 0; i < list.Count; i++) { var item = list[i]; if (predicate(item)) { list.RemoveAt(i); i--; } } return list; } } /// <summary> /// An example of using the ExcludeFilterAttribute. In this case, Action1 and Action3 require authorization but not Action2. Notice the CreateActionInvoker() override. That's necessary for the attribute to work and is probably best to put in some base class. /// </summary> [Authorize] public class ExampleController : Controller { protected override IActionInvoker CreateActionInvoker() { return new ControllerActionInvokerWithExcludeFilter(); } public ActionResult Action1() { return View(); } [ExcludeFilter(typeof(AuthorizeAttribute))] public ActionResult Action2() { return View(); } public ActionResult Action3() { return View(); } }
The example is right there. As you can see, this was pretty straightforward to do and works great. I hope it's useful to anyone?
ASP.NET MVC 5 has arrived with a very important feature called Filter Overrides. Using the Filter Overrides feature, we can exclude a specific action method or controller from the global filter or controller level filter. ASP.NET MVC 5 has arrived with a very important feature called Filter Overrides.
If we have to overload the action Method in asp.net MVC then we can not do it directly. We have to change the ActionName like this code snippet. Now to run the controller GetEmpName action method with just give the URL like this: http://localhost:49389/Home/GetEmpName.
To restrict the public action method in MVC, we can use the “NonAction” attribute. The “NonAction” attribute exists in the “System. Web.
ASP.NET MVC 4 Custom Action Filters. ASP.NET MVC provides Action Filters for executing filtering logic either before or after an action method is called. Action Filters are custom attributes that provide declarative means to add pre-action and post-action behavior to the controller's action methods.
The ASP.NET MVC, a user request is routed to the appropriate controller and action method. There is a circumstance where you want to execute some logic before or after an action method executes, hence filters are for this purpose only. Services Custom Software Development Enterprise Product Development
See my download sample and MSDN article Filtering in ASP.NET MVC. You can cancel filter execution in the OnActionExecuting and OnResultExecuting methods by setting the Result property to a non-null value.
At least nowadays, this is quite easy: to exclude all action filters from an action, just add the OverrideActionFiltersAttribute. There are similar attributes for other filters: OverrideAuthenticationAttribute, OverrideAuthorizationAttribute and OverrideExceptionAttribute.
I prefer the solution outlined here. Though it's not as generic a solution as yours, I found it a bit more straightforward.
In my case, I was looking for a way to enable a CompressionFilter on everything but a few items. So I created an empty attribute like this:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public sealed class DisableCompression : Attribute { }
Then in the main attribute, check for the presence of the attribute like so:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class CompressionFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { bool disabled = filterContext.ActionDescriptor.IsDefined(typeof(DisableCompression), true) || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(DisableCompression), true); if (disabled) return; // action filter logic here... } }
Though the page I linked to mentions that this is for MVC 3, it seems to work well enough way back in MVC 1 as well.
EDIT: showing some usage here in response to comments. Before I made the changes above, it looked exactly like this, except without the [DisableCompression] attribute flagging the method I wanted to exclude. There's no other refactoring involved.
[CompressionFilter] public abstract class BaseController : Controller { } public class SomeController : BaseController { public ActionResult WantThisActionCompressed() { // code } [DisableCompression] public ActionResult DontWantThisActionCompressed() { // code } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With