5

I am trying to look for a more clean way to add audit trail function to an exist asp.net MVC and Web Api project which contains hundreds of Controller and ApiController.

The Audit trail log would look like below. Basically I just want to log In what time who did what in this function.

UserID

ActionTime

Controller 

Action

Anything I missed ? If there is . Please correct me. Thanks.

Currently I found there are some ways to make it .

  1. Implement an ActionFilterAttribute and write my own log function in the OnActionExecuting, and then decorate all the actions with this attribute.

  2. Implement a base Controller like BaseController for all the exist controller. And write log in the OnActionExecuting. Then change all the controller to inherit from BaseController. (If it is wrong . Please correct me . Thanks.)

  3. For the ApiController. Implement a DelegatingHandler to make it.

For 1 and 2. I need change to all the exist code to make it. like change base class or decorate with new attribute. Considering in my case, This will be a hard work. Because thousands of class or methods need to be changed . I thinks it is kind of verbose. So I wondered if there is some clean way like 3 for ApiController to make it. Thanks.

Joe.wang
  • 11,537
  • 25
  • 103
  • 180
  • I think you can do this using a combination of `ActionFilters` and `MessageHandlers`. See http://stackoverflow.com/a/12300537/325521 and http://www.codeproject.com/Articles/581908/CreatingplusAdvancedplusAuditplusTrailsplususingpl source code on github => https://github.com/Rionmonster/Advanced-Auditing – Shiva Sep 25 '15 at 02:01
  • Is there anything like DelegatingHandler in the MVC Controller? so I don't need to change the exist code. Sorry. I am lazy man..Thanks. – Joe.wang Sep 25 '15 at 02:07
  • and I know the ways you mentioned is good work around. But I just try to figure out a better or Lazy way. Thanks. – Joe.wang Sep 25 '15 at 02:08
  • Can you explain how your question (and possible solution) is any different to others, where the answer was to use an [AOP toolset like PostSharp](https://www.postsharp.net/aspects)? – slugster Sep 25 '15 at 02:24
  • @slugster please help to review my comment in the Mukus answer. Thanks. – Joe.wang Sep 25 '15 at 02:30
  • 1
    These two libraries implements action filters to generate audit trails for both WebAPI and MVC, for NET Core and NET 4.5. You can take a look at the code on [Audit.MVC](https://github.com/thepirat000/Audit.NET/tree/master/src/Audit.Mvc#auditmvc) and [Audit.WebApi](https://github.com/thepirat000/Audit.NET/tree/master/src/Audit.WebApi#auditwebapi) – thepirat000 Sep 14 '16 at 03:44
  • 1
    It is a really nice library. They work very well. Thank to your help. – Joe.wang Sep 19 '16 at 07:15

2 Answers2

11

I find that using global action filters is the best way to handle cross-cutting/aspect-oriented concerns such as this.

Note that this code is not tested.

public class AuditFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var userName = HttpContext.Current.User.Identity.Name;
        var time = DateTime.Now.ToString(CultureInfo.InvariantCulture);
        var controllerName = actionContext.ControllerContext.ControllerDescriptor.ControllerName;
        var actionName = actionContext.ActionDescriptor.ActionName

        Logger.Log(string.Format("user: {0}, date: {1}, controller {2}, action {3}", userName, time, controllerName, actionName));
    }
}

And somewhere in your application startup pipeline, register the filter globally:

GlobalConfiguration.Configuration.Filters.Add(new AuditFilter());
Ronald Rogers
  • 326
  • 1
  • 5
  • This is the simplest way to intercept in the MVC. Thanks you man. – Joe.wang Sep 25 '15 at 03:25
  • Actually this one is for Web API. You would have to create a separate one for MVC that would look basically the same except you would get the Controller and Action name from the RouteData and register the filter using GlobalFilters.Filters.Add(...). – Ronald Rogers Sep 25 '15 at 03:48
  • I understand that this solution would help logging request part of it. Lets say if I want to extend this to also capture success/failure after actual controller method is executed and also log the same - any suggestions for this? – Kunal Feb 08 '19 at 04:00
2

Are you using a DI container? If you are or want to use a DI container, that could intercept all requests to the controllers. That way you don't change codes in hundreds of controllers, albeit simple change.

Here's Castle Windsor DI.

public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel;

public WindsorControllerFactory(IKernel kernel)
{
    _kernel = kernel;
}

public override void ReleaseController(IController controller)
{
    _kernel.ReleaseComponent(controller);
}

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    if (controllerType == null)
    {
        throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
    }

    return (IController)_kernel.Resolve(controllerType);
}
}

Have a look at the examples on this site if you intended to use it. I am sure there is a way to use it for both Web API and MVC controllers.

Mukus
  • 4,870
  • 2
  • 43
  • 56
  • +1 Yeah. That is a good idea to make it .I already have `Unity` in my project. But ... All the controllers are not created from the factory in the run time. I am afraid that the Intercept can not be working ...I need to do some research. Thanks. – Joe.wang Sep 25 '15 at 02:18
  • I worried about another problem which is using Unity interception will lose the context of the MVC controller and WebApiController.. I mean It will not easy to retrieve the information like route data . request and response etc. Right ? – Joe.wang Sep 25 '15 at 02:28
  • Can this help you? http://stackoverflow.com/questions/19794438/unity-dependency-injection-with-global-web-api-filter-attribute – Mukus Sep 25 '15 at 03:04
  • em....Need more time to read. But It should be an efficient way to Audit both MVC and WebApi. Thanks. – Joe.wang Sep 25 '15 at 03:27