133 lines
5.2 KiB
C#
133 lines
5.2 KiB
C#
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.Extensions.Configuration;
|
|
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace OI.Metrology.Archive;
|
|
|
|
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
|
|
public class ApiLoggingMiddleware
|
|
{
|
|
private IConfiguration Config { get; }
|
|
private readonly RequestDelegate _Next;
|
|
|
|
public ApiLoggingMiddleware(RequestDelegate next, IConfiguration config)
|
|
{
|
|
Config = config;
|
|
_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;
|
|
string pathsToLog = Config[Constants.ApiLoggingPathPrefixes];
|
|
string contentTypesToLog = Config[Constants.ApiLoggingContentTypes];
|
|
|
|
// check to see if this is a request path that is enabled for logging
|
|
if (!string.IsNullOrWhiteSpace(pathsToLog))
|
|
{
|
|
// check if the request path begins with any part of pathsToLog
|
|
if (pathsToLog.Split(';').Any(p => httpContext.Request.Path.StartsWithSegments(new PathString(p))))
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(contentTypesToLog))
|
|
{
|
|
// if there are content type filters configured, only log is the request begins with one of them
|
|
doLogging = contentTypesToLog.Split(';').Any(ct => httpContext.Request.ContentType.StartsWith(ct));
|
|
}
|
|
else
|
|
{
|
|
// if no content type filter is defined, log all content types
|
|
doLogging = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 logPath = Config[Constants.ApiLogPath];
|
|
string fileName = $"ApiLog{requestTime:yyyyMMdd_hhmmssttt}_{threadId}.txt";
|
|
|
|
HttpContext context = request.HttpContext;
|
|
|
|
if (!Directory.Exists(logPath))
|
|
_ = Directory.CreateDirectory(logPath);
|
|
|
|
using StreamWriter sw = new(Path.Join(logPath, 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>();
|
|
} |