Derek
Derek

Reputation: 8628

AS.NET WEB API Global Exception Handler Cannot Access Request Content

I'm trying to log information about the request that has caused an exception.

I'm trying to read the Content of the request attached to the ExceptionHandlerContext below :-

Although I can access other parts of the request object, its content is always empty.

It seems by this point in the pipeline it has been disposed.

Is there a way to capture the request body this far up the stack?

public class GlobalExceptionHandler : ExceptionHandler
    {
        public override void Handle(ExceptionHandlerContext context)
        {
            var exception = context.Exception;

            var test = context.Request.Content.ReadAsByteArrayAsync().Result;

            var httpException = exception as HttpException;
            if (httpException != null)
            {
                context.Result = new ErrorResult(context.Request, (HttpStatusCode)httpException.GetHttpCode(), httpException.Message);
                return;
            }

            if (exception is BusinessRuleViolationException)
            {
                context.Result = new ErrorResult(context.Request, HttpStatusCode.PaymentRequired, exception.Message);
                return;
            }

            context.Result = new ErrorResult(context.Request, HttpStatusCode.InternalServerError, exception.Message);
        }
    }

Upvotes: 3

Views: 742

Answers (1)

Derek
Derek

Reputation: 8628

For anyone that finds this thread in the future, the answer is quite simple but well hidden.

Firstly, there is a performance concern involved here.

Until i find a better solution i will be going with the following method.

The problem here is that the Request.Content property has been disposed of by the time we are at a point in the pipeline to log it in our exception handler.

The only workaround i have found so far is to read this property into a bufferstream via a message handler.

WARNINING! - We are commiting the body of the request to memory.

public class RequestBufferMessageHandler : DelegatingHandler
    {
        protected async  override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            await request.Content.LoadIntoBufferAsync();

            return await base.SendAsync(request, cancellationToken);
        }
    }

Note: You can overload this method with the maximum size of the content you wish to stream, so if there is a large file in the request it will not attempt to commit it to memory.

We need to register this as a message handler with HttpConfiguration as follows :-

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

Now, I am able to access the requests body when i try to access it in my ExceptionLogger implementation :-

public class CustomExceptionLogger : ExceptionLogger
    {
        private readonly ILog _log;

        public CustomExceptionLogger(ILogManager logManager)
        {
            _log = logManager.GetLog(typeof(CustomExceptionLogger));
        }


        public override async  Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken)
        {
            var body = await context.Request.Content.ReadAsStringAsync();


            var data = new
            {

                requestUrl = context.Request.RequestUri,
                requestQueryString = context.RequestContext.RouteData.Values,
                requestMethod = context.Request.Method,
                requestBody = body,
                exception = context.Exception.Message

            };

            _log.Error(data);

        }


    }

Upvotes: 4

Related Questions