我想记录 dotnet core 2.1 应用程序中的所有 HTTP 请求。日志记录应包括 HTTP 标头、正文和主机地址。我需要全局绑定我的日志记录代码而不更改现有代码。
我尝试了这个示例https://www.azurefromthetrenches.com/capturing-and-tracing-all-http-requests-in-c-and-net/,但没有 HTTP 事件到达侦听器。
有没有办法在 dotnet core 2.1 上全局监听 HTTP 事件?
这是一篇很好的博客文章,由 Steve Gordon 撰写,关于 HttpClient 登录 .Net Core 2.1。
本质上,您需要将 System.Net.Http.HttpClient 的日志记录级别设置为 Trace 才能获取有关请求和响应的详细信息。
appsettings.json 中所需部分的示例如下:
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"System.Net.Http.HttpClient": "Trace"
}
}
这将显示所有 HttpClient 请求和响应的所有跟踪日志记录。
您可以在中间件中记录所有http请求信息。看看下面的例子
1.创建一个类
RequestHandlerMiddleware.cs
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.IO;
using System.Threading.Tasks;
namespace Onsolve.ONE.WebApi.Middlewares
{
public sealed class RequestHandlerMiddleware
{
private readonly RequestDelegate next;
private readonly ILogger logger;
public RequestHandlerMiddleware(ILogger<RequestHandlerMiddleware> logger, RequestDelegate next)
{
this.next = next;
this.logger = logger;
}
public async Task Invoke(HttpContext context)
{
logger.LogInformation($"Header: {JsonConvert.SerializeObject(context.Request.Headers, Formatting.Indented)}");
context.Request.EnableBuffering();
var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
logger.LogInformation($"Body: {body}");
context.Request.Body.Position = 0;
logger.LogInformation($"Host: {context.Request.Host.Host}");
logger.LogInformation($"Client IP: {context.Connection.RemoteIpAddress}");
await next(context);
}
}
}
2.在
RequestHandlerMiddleware
中的
Configure
方法中添加
Startup.cs
app.UseMiddleware<RequestHandlerMiddleware>();
或更简单
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger)
{
app.Use(async (context, next) =>
{
logger.LogInformation($"Header: {JsonConvert.SerializeObject(context.Request.Headers, Formatting.Indented)}");
context.Request.EnableBuffering();
var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
logger.LogInformation($"Body: {body}");
context.Request.Body.Position = 0;
logger.LogInformation($"Host: {context.Request.Host.Host}");
logger.LogInformation($"Client IP: {context.Connection.RemoteIpAddress}");
await next.Invoke();
});
}
参考:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.2
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-2.2
由于这个问题的其他答案要么不适合我,要么看起来太复杂而无法使用,我将发布我的 ASP.NET Core 8 WebAPI 解决方案,该解决方案记录所有 REST 请求。
首先,
AddHttpLogging
必须配置要记录的 HTTP 字段,然后按 UseHttpLogging
启用配置的设置:
public class Program
{
public static void Main(string[] args)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Add HTTP logging, log all HTTP fields.
builder.Services.AddHttpLogging(logging =>
{
logging.LoggingFields = HttpLoggingFields.All;
});
var app = builder.Build();
app.UseHttpLogging();
// Configure the HTTP request pipeline.
if(app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// Use the configured HTTP logging.
app.UseHttpLogging();
app.Run();
}
}
接下来appsettings.json(或appsettings.development.json)必须配置日志记录级别以捕获:
{
"Logging": {
"LogLevel": {
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Trace"
}
}
}
这篇 Microsoft 开发博客文章更详细地解释了该主题。
尝试了下面更简单的解决方案,该解决方案在不篡改最终响应流程的情况下工作
创建中间件类来拦截所有请求和响应。在startup.cs中启用中间件类关联
app.UseMiddleware<HttpRequestResponseLogger>();
实现一个中间件类来拦截请求和响应。您可以选择将这些日志存储在数据库中。我忽略了秘密和不必要的标头值
public class HttpRequestResponseLogger
{
RequestDelegate next;
public HttpRequestResponseLogger(RequestDelegate next)
{
this.next = next;
}
//can not inject as a constructor parameter in Middleware because only Singleton services can be resolved
//by constructor injection in Middleware. Moved the dependency to the Invoke method
public async Task InvokeAsync(HttpContext context, IHttpLogRepository repoLogs)
{
HttpLog logEntry = new HttpLog();
await RequestLogger(context, logEntry);
await next.Invoke(context);
await ResponseLogger(context, logEntry);
//store log to database repository
repoLogs.SaveLog(logEntry);
}
// Handle web request values
public async Task RequestLogger(HttpContext context, HttpLog log)
{
string requestHeaders = string.Empty;
log.RequestedOn = DateTime.Now;
log.Method = context.Request.Method;
log.Path = context.Request.Path;
log.QueryString = context.Request.QueryString.ToString();
log.ContentType = context.Request.ContentType;
foreach (var headerDictionary in context.Request.Headers)
{
//ignore secrets and unnecessary header values
if (headerDictionary.Key != "Authorization" && headerDictionary.Key != "Connection" &&
headerDictionary.Key != "User-Agent" && headerDictionary.Key != "Postman-Token" &&
headerDictionary.Key != "Accept-Encoding")
{
requestHeaders += headerDictionary.Key + "=" + headerDictionary.Value + ", ";
}
}
if (requestHeaders != string.Empty)
log.Headers = requestHeaders;
//Request handling. Check if the Request is a POST call
if (context.Request.Method == "POST")
{
context.Request.EnableBuffering();
var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
context.Request.Body.Position = 0;
log.Payload = body;
}
}
//handle response values
public async Task ResponseLogger(HttpContext context, HttpLog log)
{
using (Stream originalRequest = context.Response.Body)
{
try
{
using (var memStream = new MemoryStream())
{
context.Response.Body = memStream;
// All the Request processing as described above
// happens from here.
// Response handling starts from here
// set the pointer to the beginning of the
// memory stream to read
memStream.Position = 0;
// read the memory stream till the end
var response = await new StreamReader(memStream)
.ReadToEndAsync();
// write the response to the log object
log.Response = response;
log.ResponseCode = context.Response.StatusCode.ToString();
log.IsSuccessStatusCode = (
context.Response.StatusCode == 200 ||
context.Response.StatusCode == 201);
log.RespondedOn = DateTime.Now;
// since we have read till the end of the stream,
// reset it onto the first position
memStream.Position = 0;
// now copy the content of the temporary memory
// stream we have passed to the actual response body
// which will carry the response out.
await memStream.CopyToAsync(originalRequest);
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
// assign the response body to the actual context
context.Response.Body = originalRequest;
}
}
}