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

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube