I added serilog logging to my .net core web api, but I want to extend my current middleware for displaying also a header request. here is my middleware class:
internal class SerilogRequestLoggerMiddleware
{
readonly RequestDelegate _next;
public SerilogRequestLoggerMiddleware(RequestDelegate next)
{
if (next == null) throw new ArgumentNullException(nameof(next));
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
// Push the user name into the log context so that it is included in all log entries
LogContext.PushProperty("UserName", httpContext.User.Identity.Name);
// Getting the request body is a little tricky because it's a stream
// So, we need to read the stream and then rewind it back to the beginning
string requestBody = "";
HttpRequestRewindExtensions.EnableBuffering(httpContext.Request);
Stream body = httpContext.Request.Body;
byte[] buffer = new byte[Convert.ToInt32(httpContext.Request.ContentLength)];
await httpContext.Request.Body.ReadAsync(buffer, 0, buffer.Length);
requestBody = Encoding.UTF8.GetString(buffer);
body.Seek(0, SeekOrigin.Begin);
httpContext.Request.Body = body;
Log.ForContext("RequestHeaders", httpContext.Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()), destructureObjects: true)
.ForContext("RequestBody", requestBody)
.Debug("Request information {RequestMethod} {RequestPath} information", httpContext.Request.Method, httpContext.Request.Path);
Log.Information(string.Format("Request Header: {0} ", requestBody));
Log.Information(string.Format("Request Body: {0} ", requestBody));
// The reponse body is also a stream so we need to:
// - hold a reference to the original response body stream
// - re-point the response body to a new memory stream
// - read the response body after the request is handled into our memory stream
// - copy the response in the memory stream out to the original response stream
using (var responseBodyMemoryStream = new MemoryStream())
{
var originalResponseBodyReference = httpContext.Response.Body;
httpContext.Response.Body = responseBodyMemoryStream;
//await _next(httpContext);
try
{
await _next(httpContext);
}
catch (Exception exception)
{
Guid errorId = Guid.NewGuid();
Log.ForContext("Type", "Error")
.ForContext("Exception", exception, destructureObjects: true)
.Error(exception, exception.Message + ". {@errorId}", errorId);
var result = JsonConvert.SerializeObject(new { error = exception.Message, errorId = errorId });
httpContext.Response.ContentType = "application/json";
httpContext.Response.StatusCode = 500;
await httpContext.Response.WriteAsync(result);
}
httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
var responseBody = await new StreamReader(httpContext.Response.Body).ReadToEndAsync();
httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
Log.ForContext("RequestBody", requestBody)
.ForContext("ResponseBody", responseBody)
.Debug("Response information {RequestMethod} {RequestPath} {statusCode}", httpContext.Request.Method, httpContext.Request.Path, httpContext.Response.StatusCode);
await responseBodyMemoryStream.CopyToAsync(originalResponseBodyReference);
}
}
}
I tried something like this
//byte[] bufferHeader = new byte[Convert.ToInt32(httpContext.Request.Headers.ContentLength)];
var header1 = httpContext.Request.Headers;
var bn = header1 .Values.ToList();
and I got it some 20 different objects that are not presented in my initial api call (cors, localhost address, gz, something unfamiliar to me..)
Can someone bring up the light here, what is the proper way to log header request/response for an api methods, preferably through middleware?