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.

7
  • I think you can do this using a combination of ActionFilters and MessageHandlers. See stackoverflow.com/a/12300537/325521 and codeproject.com/Articles/581908/… source code on github => github.com/Rionmonster/Advanced-Auditing Commented Sep 25, 2015 at 2: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. Commented Sep 25, 2015 at 2: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. Commented Sep 25, 2015 at 2:08
  • 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 and Audit.WebApi Commented Sep 14, 2016 at 3:44
  • 1
    It is a really nice library. They work very well. Thank to your help. Commented Sep 19, 2016 at 7:15

2 Answers 2

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());
Sign up to request clarification or add additional context in comments.

3 Comments

This is the simplest way to intercept in the MVC. Thanks you man.
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(...).
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?
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.

4 Comments

+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.
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 ?
em....Need more time to read. But It should be an efficient way to Audit both MVC and WebApi. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.