Dot Net Bug Catcher

These 8 lines of code can make debugging your ASP.Net Web API a little bit easier.

I think most us ASP.Net Web API developers have, at some point, experienced the problem where their API is returning a 500 Internal Server Error, but tracing through with Visual Studio doesn’t reveal any exceptions in our code.  This problem is often caused when a MediaTypeFormatter is unable to serialize an object.  This simple message handler can take away some of the pain of debugging these scenarios.

Dot Net Bug Catcher
Butterfly Net

The code

I’ll start with the solution, and try and explain why the problem occurs and show how this MessageHandler solves the problem.

public class BufferNonStreamedContentHandler : DelegatingHandler
{
  protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, <br>                                                         CancellationToken cancellationToken)
  {
    var response = await base.SendAsync(request, cancellationToken);
    if (response.Content != null)
    {<br>      var services = request.GetConfiguration().Services;
      var bufferPolicy = (IHostBufferPolicySelector)services.GetService(typeof(IHostBufferPolicySelector));
            <br>      // If the host is going to buffer it anyway<br>      if (bufferPolicy.UseBufferedOutputStream(response))  
      {<br>        // Buffer it now so we can catch the exception
        await response.Content.LoadIntoBufferAsync();  
      }
    }
    return response;
  }
}

This message handler is installed by adding it to the collection of MessageHandlers on the Web API configuration object.

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

Just do it sooner

High Speed Train
High Speed Train

The message handler uses the IHostBufferPolicySelector to determine if the Host will buffer the response body before sending it over the wire.  For the vast majority of cases this will be true, and definitely will be true if an API returns a CLR object that will be serialized using the JsonMediaTypeFormatter or the XmlMediaTypeFormatter.

If we know the Host is going to buffer it, then we force the content to be buffered early so that we can trigger any exceptions that will occur during serialization via user code.  This will allow Visual Studio to trap the exception.  Or if you are using an trapping errors with a message handler, the exception can be trapped and dealt with there.

Why does Web Api do that?

Normally, this check against IHostBufferPolicySelector is only done once all the message handlers have completed and control is returned to the Http Server Host.  At this point you there is no-longer user code anywhere in the call stack and therefore Visual Studio doesn’t break unless you have configured it to break in framework code.

The buffering of content is deferred by default just in case the host decides it wants to stream the content directly to the network stream.  HTTP headers need to be written out before body bytes can be written out and message handlers might want to change HTTP headers, so Web API has to wait until all the message handlers have completed before it can start streaming content.

It’s virtually free

free hugs for web api
Free Hugs

If we have buffered the content early, then the Host is smart enough to know not to try and serialize the content again, so adding this handler will add a minimal amount of overhead.  We are paying the price of checking IHostBufferPolicySelector an extra time so that we can catch exceptions for content that will be buffered.

This solution does not help if you really are serializing content directly to the network stream.  However, if you are already writing body bytes over the wire, then the status code has already been sent and there is little that can be done at that point to handle an exception cleanly. 

It can’t hurt to try it

I suspect this little trick has saved me hours of head scratching, hopefully it will do the same for others.

Image Credit: Butterfly hunter https://flic.kr/p/nqGeY7
Image Credit: High speed train https://flic.kr/p/7suAc4
Image Credit: Free hugs https://flic.kr/p/4k3eAY

Related Blog