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

  • Using Local AI to Cut Copilot Usage-Based Billing Shock

    After being gobsmacked by the new billing plan using almost all my monthly credits in one or two days, I tried pushing some Copilot-style coding work onto local models in VS Code. What I found was less "free AI" and more "pick your pain": cloud charges on one side, heavy local resource use and long waits on the other.

  • .NET 11 Preview 5 Focuses on Performance, Productivity and Safer Code

    .NET 11 Preview 5 focuses on under-the-hood runtime performance gains, streamlined APIs and language features that reduce boilerplate, plus built‑in security checks and incremental ASP.NET Core and EF Core improvements aimed at everyday developer productivity.

  • VS Code 1.124 Focuses on Agent Autonomy and Parallel Sessions

    Microsoft's June 2026 VS Code update turns on Autopilot by default and adds background sending for agent sessions.

  • Developing Agentic Systems in .NET: From Concept to Code

    ZioNet founder Alon Fliess previews his Visual Studio Live! San Diego session on building true agentic systems in .NET -- covering the cognitive loop, MCP tool integration, multi-agent orchestration and enterprise hosting and governance with the Microsoft Agent Framework.

Subscribe on YouTube