我希望自动记录每个请求。在之前的.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项目中,Global.asax WebApiConfig
GlobalConfiguration.Configure(WebApiConfig.Register);
或注册函数
那么有没有办法在.NET Core WebAPI中实现这一目标?
答案 0 :(得分:16)
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.
});
答案 1 :(得分:7)
您可以创建自己的过滤器属性...
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,那么你就可以尝试继续...
您可以全局注册过滤器(适用于所有控制器和操作) 通过将其添加到MvcOptions.Filters集合中 Startup类中的ConfigureServices方法:
告诉我们是否有效。
P.S。 这里有一个whole tutorial on intercepting requests with WebAPI,以防有人需要更多详细信息。
答案 2 :(得分:1)
演示:
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());
});
//....
}
答案 3 :(得分:1)
这是一个完整的 .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();
}
答案 4 :(得分:0)
对于需要快速(非常)肮脏的解决方案以进行调试的人(可在.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;
}
});