Home » c# » Need to log asp.net webapi 2 request and response body to a database

Need to log asp.net webapi 2 request and response body to a database

Posted by: admin November 29, 2017 Leave a comment

Questions:

I am using Microsoft Asp.net WebApi2 hosted on IIS. I very simply would like to log the request body (xml or json) and the response body for each post.

There is nothing special about this project or the controller processing the post. I am not interested in using logging frameworks like nLog, elmah, log4net, or the built in tracing features of webapi unless it is necessary to do so.

I am simply wanting to know where to put my logging code and how to get the actual json or xml from the incoming and outgoing request and response.

My controller post method:

public HttpResponseMessage Post([FromBody])Employee employee)
{
   if (ModelState.IsValid)
   {
      // insert employee into to database
   }

}
Answers:

I would recommend using a DelegatingHandler. Then you will not need to worry about any logging code in your controllers.

public class LogRequestAndResponseHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // log request body
        string requestBody = await request.Content.ReadAsStringAsync();
        Trace.WriteLine(requestBody);

        // let other handlers process the request
        var result = await base.SendAsync(request, cancellationToken);

        if (result.Content != null)
        {
            // once response body is ready, log it
            var responseBody = await result.Content.ReadAsStringAsync();
            Trace.WriteLine(responseBody);
        }

        return result;
    }
}

Just replace Trace.WriteLine with your logging code and register the handler in WebApiConfig like this:

config.MessageHandlers.Add(new LogRequestAndResponseHandler());

Questions:
Answers:

There are multiple approaches to generically handle Request/Response logging for every WebAPI method calls:

  1. ActionFilterAttribute:
    One can write custom ActionFilterAttribute and decorate the controller/action methods to enable logging.

    Con: You need to decorate every controller/methods (still you can do it on base controller, but still it doesn’t address cross cutting concerns.

  2. Override BaseController and handle logging there.

    Con: We are expecting/forcing the controllers to inherit from a custom base controller.

  3. Using DelegatingHandler.

    Advantage: We are not touching controller/method here with this approach. Delegating handler sits in isolation and gracefully handles the request/response logging.

For more indepth article, refer this http://weblogs.asp.net/fredriknormen/log-message-request-and-response-in-asp-net-webapi.

Questions:
Answers:

One of the option you have is using creating a action filter and decorating your WebApiController/ApiMethod with it.

Filter Attribute

public class MyFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.Request.Method == HttpMethod.Post)
            {
                var postData = actionContext.ActionArguments;
                //do logging here
            }
        }
    }

WebApi controller

[MyFilter]
public class ValuesController : ApiController{..}

or

[MyFilter]
public void Post([FromBody]string value){..}

Hope this helps.

Questions:
Answers:

Getting access to request message is easy. Your base class, ApiController contains .Request property, which, as name suggests, contains the request in parsed form. You simply examine it for whatever you’re looking to log and pass it to your logging facility, whichever it may be. This code you can put in the beginning of your action, if you need to do it for just one or a handful.

If you need to do it on all actions (all meaning more than a manageable handful), then what you can do is override .ExecuteAsync method to capture every action call for your controller.

public override Task<HttpResponseMessage> ExecuteAsync(
    HttpControllerContext controllerContext,
    CancellationToken cancellationToken
)
{
    // Do logging here using controllerContext.Request
    return base.ExecuteAsync(controllerContext, cancellationToken);
}