git checkout -b feature-branch
This commit is contained in:
124
Server/ApiLoggingMiddleware.cs
Normal file
124
Server/ApiLoggingMiddleware.cs
Normal file
@ -0,0 +1,124 @@
|
||||
using OI.Metrology.Server.Models;
|
||||
using System.Text;
|
||||
|
||||
namespace OI.Metrology.Server;
|
||||
|
||||
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
|
||||
public class ApiLoggingMiddleware
|
||||
{
|
||||
private readonly AppSettings _AppSettings;
|
||||
private readonly RequestDelegate _Next;
|
||||
|
||||
public ApiLoggingMiddleware(RequestDelegate next, AppSettings appSettings)
|
||||
{
|
||||
_AppSettings = appSettings;
|
||||
_Next = next;
|
||||
}
|
||||
|
||||
// this is the method called in ASP.NET Core middleware to handle an HTTP request
|
||||
// the middleware allows you to add code to run before and after an http request is handled
|
||||
// they are stacked together like a pipeline
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool doLogging = false;
|
||||
// check to see if this is a request path that is enabled for logging
|
||||
if (!string.IsNullOrWhiteSpace(_AppSettings.ApiLoggingPathPrefixes))
|
||||
{
|
||||
// check if the request path begins with any part of pathsToLog
|
||||
if (_AppSettings.ApiLoggingPathPrefixes.Split(';').Any(p => httpContext.Request.Path.StartsWithSegments(new PathString(p))))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_AppSettings.ApiLoggingContentTypes))
|
||||
{
|
||||
// if no content type filter is defined, log all content types
|
||||
doLogging = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if there are content type filters configured, only log is the request begins with one of them
|
||||
string? contentType = httpContext.Request.ContentType;
|
||||
doLogging = contentType is not null && _AppSettings.ApiLoggingContentTypes.Split(';').Any(ct => contentType.StartsWith(ct));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if logging is enabled for this request
|
||||
if (doLogging)
|
||||
{
|
||||
DateTime startTime = DateTime.Now;
|
||||
|
||||
// get request body
|
||||
string requestBodyContent = await ReadRequestBody(httpContext.Request);
|
||||
|
||||
// save the original response and stream
|
||||
Stream originalBodyStream = httpContext.Response.Body;
|
||||
|
||||
using MemoryStream responseBody = new();
|
||||
// replace response stream with our memory stream
|
||||
httpContext.Response.Body = responseBody;
|
||||
|
||||
// call next middleware, this is to process the request so we have a response to inspect
|
||||
await _Next(httpContext);
|
||||
|
||||
// get response body into string
|
||||
_ = httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
|
||||
string bodyAsText = await new StreamReader(httpContext.Response.Body).ReadToEndAsync();
|
||||
|
||||
// copy memory stream to original response stream
|
||||
_ = httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
|
||||
await httpContext.Response.Body.CopyToAsync(originalBodyStream);
|
||||
|
||||
// log the request and response
|
||||
LogRequestAndResponse(startTime, httpContext.Request, requestBodyContent, bodyAsText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine("Error in ApiLoggingMiddleware: " + ex.ToString());
|
||||
}
|
||||
|
||||
// proceed with the http request normally
|
||||
await _Next(httpContext);
|
||||
}
|
||||
|
||||
private static async Task<string> ReadRequestBody(HttpRequest request)
|
||||
{
|
||||
HttpRequestRewindExtensions.EnableBuffering(request);
|
||||
|
||||
byte[] buffer = new byte[Convert.ToInt32(request.ContentLength)];
|
||||
_ = await request.Body.ReadAsync(buffer);
|
||||
string bodyAsText = Encoding.UTF8.GetString(buffer);
|
||||
_ = request.Body.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return bodyAsText;
|
||||
}
|
||||
|
||||
private void LogRequestAndResponse(DateTime requestTime, HttpRequest request, string requestBody, string responseBody)
|
||||
{
|
||||
int threadId = Environment.CurrentManagedThreadId;
|
||||
|
||||
string fileName = $"ApiLog{requestTime:yyyyMMdd_hhmmssttt}_{threadId}.txt";
|
||||
|
||||
HttpContext context = request.HttpContext;
|
||||
|
||||
if (!Directory.Exists(_AppSettings.ApiLogPath))
|
||||
_ = Directory.CreateDirectory(_AppSettings.ApiLogPath);
|
||||
|
||||
using StreamWriter sw = new(Path.Join(_AppSettings.ApiLogPath, fileName), true);
|
||||
sw.WriteLine($"Request at {requestTime:yyyy/MM/dd hh:mm:ss.ttt} from {context.Connection.RemoteIpAddress}");
|
||||
sw.WriteLine($"{request.Method} {request.Path} {request.QueryString}");
|
||||
sw.WriteLine("Request body:");
|
||||
sw.WriteLine(requestBody);
|
||||
sw.WriteLine($"Response at {DateTime.Now:yyyy/MM/dd hh:mm:ss.ttt}");
|
||||
sw.WriteLine(responseBody);
|
||||
sw.WriteLine("==========");
|
||||
}
|
||||
}
|
||||
|
||||
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||
public static class ApiLoggingMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseApiLoggingMiddleware(this IApplicationBuilder builder) => builder.UseMiddleware<ApiLoggingMiddleware>();
|
||||
}
|
Reference in New Issue
Block a user