ASP.NET MVC 3 Preview 1 – Global and Conditional Filters
Posted by: Malcolm Sheridan ,
on 9/22/2010,
in
Category ASP.NET MVC
Abstract: The following article demonstrates how to use global filters in ASP.NET MVC 3 Preview 1 and also how to use them conditionally.
Another great feature to be introduced into ASP.NET MVC 3 Preview 1 is global filters. Global filters are a way of registering controller or action level filters in the one place. The advantage of this is you no longer need to decorate every controller or action with an attribute that you wanted executed whenever it ran, now you can register is one place and have it executed across multiple controllers and action. To see this in action, you'll need to download ASP.NET MVC 3 Preview 1 which can be found here. For beta software, I always recommend installing this on a virtual machine, just in case something goes wrong.
Open studio 2010 and create a new ASP.NET MVC 3 Web Application (Razor) project. This example doesn’t get into the Razor syntax, but that is the default view engine I am using currently. Let’s begin with creating a new action filter that sends debug data to the HttpResponse object that prints to the screen diagnostic information such as what controller was called, what action was called and how long the code took to execute. I’ve created a class called DebugTimerAttribute. Here’s the code below:
public class DebugTimerAttribute : ActionFilterAttribute
{
readonly Stopwatch _startWatch = new Stopwatch();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_startWatch.Start();
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
_startWatch.Stop();
filterContext.HttpContext.Response.Write(string.Format("Controller ({0}) Action ({1}) Ellapsed Time ({2})",
filterContext.RouteData.Values["controller"],
filterContext.RouteData.Values["action"],
_startWatch.ElapsedMilliseconds));
}
}
There’s nothing special about the code above. What is special is how I can now use this. Currently in ASP.NET MVC 1 and 2, I’d need to decorate my controller’s or action’s if I wanted to use this filter:
[DebugTimer]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
There’s no issue in doing this. What can happen is you have multiple controllers and action and need this filter applied to all of them. If you didn’t have a base controller, you’d have a tonne of refactoring to do. Thankfully in ASP.NET MVC 3 Preview 1, you can now use global filters. This give you the power to register to filter in one spot and have it applied to all controller’s and actions. To use global filters, open the global.asax file. You’ll find a new static method called RegisterGlobalFilters. This registers filters that you want to apply to all controllers and actions. This is how I’ll add my new filter:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new DebugTimerAttribute());
}
The code above registers both filters, so when you run the page, you’ll see the debug information printed out on the screen.
That’s great if you wanted it applied to all controllers and actions. But what if you wanted it applied only on certain conditions, such as when you’re debugging you need this information, but when you deploy to production, you don’t want your users seeing that. This is when you need to create a custom filter provider. Your class has to implement IFilterProvider and you place your login inside of the GetFilters method. Here’s one way to implement a custom filter provider:
public class DebugFilterProvider : IFilterProvider
{
private readonly List<Func<ControllerContext, object>> _conditions = new List<Func<ControllerContext, object>>();
public void Add(Func<ControllerContext, object> condition)
{
_conditions.Add(condition);
}
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return from condition in _conditions
select condition(controllerContext) into filter
where filter != null
select new Filter(filter, FilterScope.Global);
}
}
GetFilters loops through all the conditions, which is a generic list which contains a delegate, and if that delegate is not null, then it will create a new Filter object and return that. So in your global.asax file, you can add this code so this filter only runs when you have debugging enabled:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
var provider = new DebugFilterProvider();
provider.Add(c => c.HttpContext.IsDebuggingEnabled ? new DebugTimerAttribute() : null);
FilterProviders.Providers.Add(provider);
}
Or if you only wanted it applied to an action named About, you’d do this:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
var provider = new DebugFilterProvider();
provider.Add(c => c.RouteData.Values["action"].ToString() == "About" ? new DebugTimerAttribute() : null);
FilterProviders.Providers.Add(provider);
}
Global filters are a nice way to centralise functionality in your project.
The entire source code of this article can be downloaded over here
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Malcolm Sheridan is a Microsoft awarded MVP in ASP.NET, a Telerik Insider and a regular presenter at conferences and user groups throughout Australia and New Zealand. Being an ASP.NET guy, his focus is on web technologies and has been for the past 10 years. He loves working with ASP.NET MVC these days and also loves getting his hands dirty with jQuery and JavaScript. He also writes technical articles on ASP.NET for SitePoint and other various websites. Follow him on twitter @
malcolmsheridan