Reputation: 8628
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
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