Practical .NET

Processing Every Request and Response in ASP.NET

Every once in a while, I have some function that I want performed by several different Action methods. If those Actions are in several different controllers, then the problem is probably best referred to as a "cross-cutting concern." A cross-cutting concern is something that applies in many places in your application but not every place. Typical examples are authentication and logging: You want to lock users out of some Action methods and not others, and you want to log activity on some Controllers and not others.

There are several ways to address this problem -- adding a method to some class and calling that method from the start of each Action method just being the most obvious. The good news here is that all your other options are easier to implement and maintain.

For Ad Hoc Scenarios
One solution is to package the code you want to execute into your own custom Action Filter attribute and apply that attribute to the places where you want. This is the solution that makes the most sense when the Action methods you want to apply this functionality to are truly random (that is, not all the methods in all the controllers) or where you want to be able to remove the functionality easily. For example, if you add logging to a method to track down a problem, you can turn off that logging just by removing the attribute from the method.

To create a custom Action Filter, just create a class that inherits from ActionFilterAttribute and override the OnActionExecuting method and/or the OnActitionExecuted methods. Your OnActionExecuting method will be called for you automatically before your Action method is called. That method is passed a property-rich ActionExecutingContext object that gives you access to all sorts of information about the request.

On the other hand, the OnActionExecuted method is called after your Action method has executed, letting you work with the output of your Action method. The OnActionExecuted method is passsed the ActionExecutedContext object. As an example, the Result property on the ActionExecutedContext object gives you access to the View returned by the Action method.

A skeleton custom Action filter looks like this:

public class GenericActionFilter: ActionFilterAttribute
{
  public override void OnActionExecuting(ActionExecutingContext filterContext)
  {
    //...work with the filterContext object before executing the method
  }

  public override void OnActionExecuted(ActionExecutedContext filterContext)
  {
    //...work with the filterContext object after executing the method
  }
}

You can add a constructor and properties to your Action filter to allow you to pass values to your attribute when you apply it to an Action method. Within your methods, you can use those values to customize your attribute's behavior.

This code, for example, has a constructor that accepts a parameter called Type and a property called priority:

public class GenericActionFilter: ActionFilterAttribute
{
  public int Priority { get; set; }
  public GenericActionFilter(string Type)
 {...}

To use this attribute on an Action method, you would apply it like this:

[GenericActionFilter("ImportantType", Priority = 3)]
public ActionResult Index()

For Whole Controllers
If you want to apply some additional functionality to whole controllers, you can create a class that inherits either from the Controller class (for ASP.NET MVC applications) or the ApiController class (for ASP.NET Web API applications). You can then have your MVC or Web API Controllers inherit from your new base class. This is a better solution when you really do want to apply a change to every controller in your site.

Within your new base class you'll find you have any number of methods that you can override to provide additional functionality to your Controllers. For example, your base class has overridable OnActionExecuting and OnActionExecuted methods that work like the equivalent methods in Action Filter attributes, even to being passed ActionExecutingContext and ActionExecutedContext objects (for other methods in your base class, the Controller's HttpContext, Request and Response properties give you access to information about the current request).

You're also free to override the helper methods built into the base class that you normally call from your Action methods: View, PartialView, File, RedirectToResult and so on. In your version of the method you can modify the request made by the class, call the base method in the System.Web.Mvc.Controller class, and then return the result to the caller, perhaps with some additional modifications.

This technique is so generally useful that I recommend to my clients that they create a base Controller or ApiController class when they set up a new application and have all their application's Controllers inherit from that class, even if they can't currently imagine a use for this class. It's less work to add functionality to every controller if you've got your base class in place.

At the site level, you have a third option: HTTP Modules in ASP.NET MVC and Message Handlers in ASP.NET Web API. They're sufficiently interesting, however, that I'll devote a separate column to them.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

  • What's New for Python, Java in Visual Studio Code

    Microsoft announced March 2024 updates to its Python and Java extensions for Visual Studio Code, the open source-based, cross-platform code editor that has repeatedly been named the No. 1 tool in major development surveys.

Subscribe on YouTube