The ASP.NET MVC Framework lets Controller actions perform the actual logic for the coordination of work between the (domain) model and the views of the web application. The actions themselves can be filtered if you want to. There are a couple of sample implementations out there that show you how you can limit actions to particular HTTP verbs or use Role Based Security to only allow particular roles to execute an action.
Yet, filtering is not the only useful work that an action filter can do. Logging is one of them. This made me think of the health monitoring (more on that in my slidedeck on Health Monitoring in ASP.NET) and how it would be nice to see some WebEvent come along for every controller action that is called. I whipped up a small piece of code that I put into my Developer Days demo pack and will explain in this post.
First, a bit of background in case you are not familiar with action filters. Action filters get a chance to intercept the call to an action before or after the action has executed, – or -before or after an action result is executed. The first two moments are always available, the second two only when your controller action returns an ActionResult. The implementation of an action filter is performed in a special attribute class. The attribute can then be applied to all relevant actions or the controller class. In the latter case all actions will have that particular action filter automatically. Here’s an example for the HealthMonitoringActionFilter that we will create:
An action filter is a ActionFilterAttribute derived class that can override a couple of methods corresponding to the four moments of interception:
- OnActionExecuting (ActionExecutingContext)
- OnActionExecuted (ActionExecutedContext)
- OnResultExecuting (ResultExecutingContext)
- OnResultExecuted (ResultExecutedContext)
Above you can see the argument type shown in italics. All context classes derive from ControllerContext and give access to the RouteData, Controller and HttpContext because of that. They also present you with properties for the ActionResult that comes from this controller action.
You can pick the moment of filter execution you desire and do your thing there. The _ing methods allow you to cancel the actual execution that would follow. The _ed methods are after the fact but offer additional information such as the exception (Exception property) that may have occurred and whether is was handled by the filter (ExceptionHandled property).
In our case we want to send out a health monitoring event whenever a controller action fires. To start, let’s create that event class, by deriving from System.Web.Management.WebManagementEvent and overriding the necessary methods.
The MvcActionEvent class has two additional properties for the current Action and controller type. I haven’t implemented the IncrementPerfCounters for real, but just imagine that some nifty counters get a bump of one. The extra properties are appended to the output of events details via the FormatCustomEventDetails method.
The last bit of code that we’ll need is the actual ActionFilter attribute. Since we want to raise the Mvc action event after the action has executed. Therefore, an override to the OnActionExecuted method is needed where the MvcActionEvent is raised.
At this point we do not care about the ActionResult being executed. If you would care about it, create another event class (e.g. MvcActionResultEvent), override the OnResultExecuted and raise the event. Simple as that.
There you go. Just apply this action filter attribute to whatever controller or action(s) you want. Remember to change your web.config to register the new event class and to create a new rule that uses it.
I chose to use the SqlWebEventProvider, but any other event provider (read: sink) could have been used, obviously.