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

  • IDE Irony: Coding Errors Cause 'Critical' Vulnerability in Visual Studio

    In a larger-than-normal Patch Tuesday, Microsoft warned of a "critical" vulnerability in Visual Studio that should be fixed immediately if automatic patching isn't enabled, ironically caused by coding errors.

  • Building Blazor Applications

    A trio of Blazor experts will conduct a full-day workshop for devs to learn everything about the tech a a March developer conference in Las Vegas keynoted by Microsoft execs and featuring many Microsoft devs.

  • Gradient Boosting Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the gradient boosting regression technique, where the goal is to predict a single numeric value. Compared to existing library implementations of gradient boosting regression, a from-scratch implementation allows much easier customization and integration with other .NET systems.

  • Microsoft Execs to Tackle AI and Cloud in Dev Conference Keynotes

    AI unsurprisingly is all over keynotes that Microsoft execs will helm to kick off the Visual Studio Live! developer conference in Las Vegas, March 10-14, which the company described as "a must-attend event."

  • Copilot Agentic AI Dev Environment Opens Up to All

    Microsoft removed waitlist restrictions for some of its most advanced GenAI tech, Copilot Workspace, recently made available as a technical preview.

Subscribe on YouTube