This is an evolution of my previous post in which I described a method for inspecting the body of a request in a middleware method and passing it out so it could be logged later if required.
The below code reads in the body of a request and then if the response indicates failure (I’m also only interested in POST requests) the body is pushed as an additional property to the Serilog log.
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
public ExceptionHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
string requestContent = new StreamReader(httpContext.Request.Body).ReadToEnd();
string requestMethod = httpContext.Request.Method;
httpContext.Request.Body.Position = 0;
Stream responseBody = httpContext.Response.Body;
try
{
using (MemoryStream memoryStream = new MemoryStream())
{
httpContext.Response.Body = memoryStream;
await _next(httpContext);
int responseStatus = httpContext.Response.StatusCode;
if (!IsSuccessStatusCode(httpContext.Response.StatusCode) && requestMethod == "POST")
{
// Get response body if required
//memoryStream.Position = 0;
//string responseContent = new StreamReader(memoryStream).ReadToEnd();
using (LogContext.PushProperty("RequestBody", requestContent))
{
Log.Warning("Invalid request ({@ResponseStatusCode}): {@RequestPath}", httpContext.Response.StatusCode, httpContext.Request.Path.Value);
}
}
memoryStream.Position = 0;
await memoryStream.CopyToAsync(responseBody);
}
}
finally
{
httpContext.Response.Body = responseBody;
}
}
public bool IsSuccessStatusCode(int statusCode)
{
if ((statusCode >= 200) && (statusCode <= 299))
{
return true;
}
else
{
return false;
}
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class ExceptionHandlingMiddlewareExtensions
{
public static IApplicationBuilder UseExceptionHandlingMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ExceptionHandlingMiddleware>();
}
}
To use the above middleware it needs to be enabled in the Configure method of your Startup.cs file. By enabling buffering on the request the request body is upgraded to a FileBufferingStream which means that it goes from being a forward only stream that doesn’t support seeking or reading the stream a second time to one that does.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Use(async (context, next) => {
context.Request.EnableBuffering();
await next();
});
app.UseMiddleware<ExceptionHandlingMiddleware>();
// Your other code...
}
0 Comments