0

I wondering if it somehow is possible to implement logging when the request (in JSON format) being transformed to object and object is transformed to json in response (in the model binding) ? I want to avoid to implement logging in the controllers to have more clean code, if it's possible.

EDIT:

I need to log the whole incoming and outgoing json string.

Also i'm using ASP.NET Web api 2.

Henrik
  • 1,797
  • 4
  • 22
  • 46
  • I'm not sure what you mean, do you want to log what happens in the model binder? – user247702 May 21 '14 at 15:00
  • Yes, that's exactly what i meant. I want to log incoming request data and also response data.. sorry for my explanation. – Henrik May 21 '14 at 15:02

1 Answers1

1

Sure, just override the default model binder.

public class LoggingDataBinder : DefaultModelBinder
{

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
         var request = context.HttpContext.Request;
         //if you only want to deal with json requests
         if (request.ContentType == "application/json")
         {
              //can now access QueryString, URL, or InputStream
              using (var sr = new StreamReader(request.InputStream))
              {
                 var jsonData = sr.ReadToEnd();
                 LogMethod(jsonData);
              }    
         }
         return base.BindModel(controllerContext, bindingContext);
    }

} 

Then in Global.asax.cs.Application_Start():

protected void Application_Start(Object sender, EventArgs e)
{
     ModelBinders.Binders.DefaultBinder = new LoggingModelBinder();
}

You can customize this in various ways (having this custom binder for only specific types, for specific requests, grab details from the request to log), but this would satisfy your question of how to log when model binding occurs.

EDIT

To handle logging responses, I'd suggest overriding OnResultExecuted in your controller (or base controller) and doing like so:

protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
    //assuming this will be the type of all of your JSON data actions
    var json = filterContext.Result as JsonResult;
    if (json != null)
    {
         //assuming you haven't overriden the default serializer here,
         //otherwise may be inconsistent with what the client sees
         LogMethod(new JavaScriptSerializer().Serialize(json.Data));
    }
}

Instead of doing it at this level, you could create your own ActionFilterAttribute and decorate your controllers/action methods appropriately (e.g. only decorate action methods that return JsonResult). Here's an old article from MSDN that would still put you on the right path to getting that done.

EDIT 2 I'm inexperienced with Web API, but I believe your best bet would be to implement a message handler:

public class JsonMessageLoggerHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        bool isJson = false;
        //not tested, you may have to play with this
        if (request.Content != null && request.Content.Headers.ContentType.MediaType == "application/json")
        {
            isJson = true;
            var requestText = await request.Content.ReadAsStringAsync();
            LogMethod(requestText);
        }
        // Call the inner handler.
        var response = await base.SendAsync(request, cancellationToken);

        //log the response
        if (isJson)
        {
            var responseText = await response.Content.ReadAsStringAsync();
            LogMethod(responseText);
        }

        return response;
    }
}

Then wire up your message handler with the WebApiConfig class:

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

Again, I'm not experienced with Web API so you may need to play around with this (and this frankly might not be the best way to go about this). Here are some links for more details: SO Question and ASP.NET

Community
  • 1
  • 1
Sven Grosen
  • 5,616
  • 3
  • 30
  • 52