如何在 .NET Core WebAPI 中自动记录每个请求?
Posted
技术标签:
【中文标题】如何在 .NET Core WebAPI 中自动记录每个请求?【英文标题】:How to auto log every request in .NET Core WebAPI? 【发布时间】:2018-01-10 18:09:47 【问题描述】:我希望自动记录每个请求。在之前的 .Net Framwork WebAPI 项目中,我曾经注册了一个 delegateHandler 来实现。
WebApiConfig.cs
public static void Register(HttpConfiguration config)
config.MessageHandlers.Add(new AutoLogDelegateHandler());
AutoLogDelegateHandler.cs
public class AutoLogDelegateHandler : DelegatingHandler
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var requestBody = request.Content.ReadAsStringAsync().Result;
return await base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
HttpResponseMessage response = task.Result;
//Log use log4net
_LogHandle(request, requestBody, response);
return response;
);
日志内容示例:
------------------------------------------------------
2017-08-02 19:34:58,840
uri: /emp/register
body:
"timeStamp": 1481013427,
"id": "0322654451",
"type": "t3",
"remark": "system auto reg"
response: "msg":"c556f652fc52f94af081a130dc627433","success":"true"
------------------------------------------------------
但在.NET Core WebAPI项目中,没有WebApiConfig
,或者Global.asaxGlobalConfiguration.Configure(WebApiConfig.Register);
处的注册功能
那么有什么方法可以在 .NET Core WebAPI 中实现这一点吗?
【问题讨论】:
【参考方案1】:ActionFilter
将一直工作,直到您需要记录仅由 MVC 中间件处理的请求(作为控制器操作)。
如果您需要记录所有传入请求,则需要使用中间件方法。
良好的视觉效果explanation:
请注意,中间件顺序很重要,如果您的日志记录应该在管道执行开始时完成,那么您的中间件应该是第一个。
来自docs的简单示例:
public void Configure(IApplicationBuilder app)
app.Use(async (context, next) =>
// Do loging
// Do work that doesn't write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
);
【讨论】:
如果我的 api 在 AWS Api Gateway 上运行,这是否有效……这是我的问题,我需要一些帮助……***.com/questions/59167427/… 我想你的意思是 ActionFilter 只能用于记录 MVC 中间件处理的请求?【参考方案2】:您可以创建自己的过滤器属性...
public class InterceptionAttribute : ActionFilterAttribute
public override void OnActionExecuting(HttpActionContext actionContext)
var x = "This is my custom line of code I need executed before any of the controller actions, for example log stuff";
base.OnActionExecuting(actionContext);
...您可以在 GlobalFilters 中注册它,但由于您说您使用的是 .NET Core,因此您可以尝试继续...
来自docs.microsoft.com:
您可以全局注册过滤器(适用于所有控制器和操作) 通过将其添加到 MvcOptions.Filters 集合中 Startup 类中的 ConfigureServices 方法:
让我们知道它是否有效。
附: 这是whole tutorial on intercepting requests with WebAPI,以防有人需要更多详细信息。
【讨论】:
是的,它有效。我创建了一个AutoLogAttribute
,并将每个请求记录在OnActionExecuted
。然后通过将过滤器添加到 Startup 类的 ConfigureServices 方法中的 MvcOptions.Filters 集合来全局注册过滤器。
@Eedoh 你是怎么做到的?【参考方案3】:
对于想要一个快速且(非常)肮脏的解决方案用于调试目的的人(适用于 .Net Core 3),这里是 this answer 的扩展,这就是您所需要的......
app.Use(async (context, next) =>
var initialBody = context.Request.Body;
using (var bodyReader = new StreamReader(context.Request.Body))
string body = await bodyReader.ReadToEndAsync();
Console.WriteLine(body);
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body));
await next.Invoke();
context.Request.Body = initialBody;
);
【讨论】:
谢谢。您能在这里解释一下正文内容操作背后的原因吗? @GGleGrand 已经有一段时间了,但我猜这是因为代码试图读取流两次——一次是在这里登录,一次是“真正的”。所以我需要在记录后重置流。【参考方案4】:演示:
AutologArribute.cs(新文件)
/// <summary>
/// <see cref="https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters#Dependency injection"/>
/// </summary>
public class AutoLogAttribute : TypeFilterAttribute
public AutoLogAttribute() : base(typeof(AutoLogActionFilterImpl))
private class AutoLogActionFilterImpl : IActionFilter
private readonly ILogger _logger;
public AutoLogActionFilterImpl(ILoggerFactory loggerFactory)
_logger = loggerFactory.CreateLogger<AutoLogAttribute>();
public void OnActionExecuting(ActionExecutingContext context)
// perform some business logic work
public void OnActionExecuted(ActionExecutedContext context)
//TODO: log body content and response as well
_logger.LogDebug($"path: context.HttpContext.Request.Path");
StartUp.cs
public void ConfigureServices(IServiceCollection services)
//....
// https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters#filter-scopes-and-order-of-execution
services.AddMvc(opts=>
opts.Filters.Add(new AutoLogAttribute());
);
//....
【讨论】:
【参考方案5】:这是 .NET Core 2.2 Web API 的完整日志组件。 它将记录请求和响应,包括标头和正文。 只需确保您有一个“日志”文件夹即可。
AutoLogMiddleWare.cs(新文件)
public class AutoLogMiddleWare
private readonly RequestDelegate _next;
public AutoLogMiddleWare(RequestDelegate next)
_next = next;
public async Task Invoke(HttpContext context)
try
string route = context.Request.Path.Value;
string httpStatus = "0";
// Log Request
var originalRequestBody = context.Request.Body;
originalRequestBody.Seek(0, SeekOrigin.Begin);
string requestBody = new StreamReader(originalRequestBody).ReadToEnd();
originalRequestBody.Seek(0, SeekOrigin.Begin);
// Log Response
string responseBody = string.Empty;
using (var swapStream = new MemoryStream())
var originalResponseBody = context.Response.Body;
context.Response.Body = swapStream;
await _next(context);
swapStream.Seek(0, SeekOrigin.Begin);
responseBody = new StreamReader(swapStream).ReadToEnd();
swapStream.Seek(0, SeekOrigin.Begin);
await swapStream.CopyToAsync(originalResponseBody);
context.Response.Body = originalResponseBody;
httpStatus = context.Response.StatusCode.ToString();
// Clean route
string cleanRoute = route;
foreach (var c in Path.GetInvalidFileNameChars())
cleanRoute = cleanRoute.Replace(c, '-');
StringBuilder sbRequestHeaders = new StringBuilder();
foreach (var item in context.Request.Headers)
sbRequestHeaders.AppendLine(item.Key + ": " + item.Value.ToString());
StringBuilder sbResponseHeaders = new StringBuilder();
foreach (var item in context.Response.Headers)
sbResponseHeaders.AppendLine(item.Key + ": " + item.Value.ToString());
string filename = DateTime.Now.ToString("yyyyMMdd.HHmmss.fff") + "_" + httpStatus + "_" + cleanRoute + ".log";
StringBuilder sbLog = new StringBuilder();
sbLog.AppendLine("Status: " + httpStatus + " - Route: " + route);
sbLog.AppendLine("=============");
sbLog.AppendLine("Request Headers:");
sbLog.AppendLine(sbRequestHeaders.ToString());
sbLog.AppendLine("=============");
sbLog.AppendLine("Request Body:");
sbLog.AppendLine(requestBody);
sbLog.AppendLine("=============");
sbLog.AppendLine("Response Headers:");
sbLog.AppendLine(sbResponseHeaders.ToString());
sbLog.AppendLine("=============");
sbLog.AppendLine("Response Body:");
sbLog.AppendLine(responseBody);
sbLog.AppendLine("=============");
var path = Directory.GetCurrentDirectory();
string filepath = ($"path\\Logs\\filename");
File.WriteAllText(filepath, sbLog.ToString());
catch (Exception ex)
// It cannot cause errors no matter what
public class EnableRequestRewindMiddleware
private readonly RequestDelegate _next;
public EnableRequestRewindMiddleware(RequestDelegate next)
_next = next;
public async Task Invoke(HttpContext context)
context.Request.EnableRewind();
await _next(context);
public static class EnableRequestRewindExtension
public static IApplicationBuilder UseEnableRequestRewind(this IApplicationBuilder builder)
return builder.UseMiddleware<EnableRequestRewindMiddleware>();
Startup.cs(现有文件)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IMapper mapper, ILoggerFactory loggerFactory)
bool isLogEnabled = true; // Replace it by some setting, Idk
if(isLogEnabled)
app.UseEnableRequestRewind(); // Add this first
(...)
if(isLogEnabled)
app.UseMiddleware<AutoLogMiddleWare>(); // Add this just above UseMvc()
app.UseMvc();
【讨论】:
以上是关于如何在 .NET Core WebAPI 中自动记录每个请求?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Asp.Net Core 3.0 WebAPI 中启用 CORS
如何在 Asp Net Core Web App(不是 WebAPI)中存储令牌
如何在 ASP.net Core WebAPI 中启用 CORS
如何在.net core web 中将 url 设置为 webapi?