以前 .NET Framework WebApi 记录接口访问日志,一般是通过Filter的方式进行拦截,通过重写ActionFilterAttribute的OnActionExecuting实现拦截记录Request内容,通过重写OnActionExecuted实现拦截记录Response内容,具体实现代码就不贴了。这篇简单介绍.Net Core WebApi 下通过中间件的拦截方式记录接口访问日志,关键部分是通过读取获取 Request.Body 时需要开启 Request.EnableRewind () 启用倒带功能;读取 Response.Body 时需要用到的技巧,详细看代码。该例子中我使用的日志组件是Log4Net,获取到的信息通过Log4Net保存到本地文件。
创建日志类
using System;
using System.Collections.Generic;
using System.Linq;
namespace DYDGame.Web.Host
{
public class RequestResponseLog
{
public string Url {get;set;}
public IDictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
public string Method { get; set; }
public string RequestBody { get; set; }
public string ResponseBody { get; set; }
public DateTime ExcuteStartTime { get; set; }
public DateTime ExcuteEndTime { get; set; }
public override string ToString()
{
string headers = "[" + string.Join(",", this.Headers.Select(i => "{" + $"\"{i.Key}\":\"{i.Value}\"" + "}")) + "]";
return $"Url: {this.Url},\r\nHeaders: {headers},\r\nMethod: {this.Method},\r\nRequestBody: {this.RequestBody},\r\nResponseBody: {this.ResponseBody},\r\nExcuteStartTime: {this.ExcuteStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff")},\r\nExcuteStartTime: {this.ExcuteEndTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}";
}
}
}
创建记录接口日志中间件 Middleware
using System;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text;
using System.Threading;
using DYDGame.Utility;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
namespace DYDGame.Web.Host {
public class RequestResponseLoggingMiddleware {
private readonly RequestDelegate _next;
private RequestResponseLog _logInfo;
public RequestResponseLoggingMiddleware (RequestDelegate next) {
_next = next;
}
public async Task Invoke (HttpContext context) {
_logInfo = new RequestResponseLog ();
HttpRequest request = context.Request;
_logInfo.Url = request.Path.ToString ();
_logInfo.Headers = request.Headers.ToDictionary (k => k.Key, v => string.Join (";", v.Value.ToList ()));
_logInfo.Method = request.Method;
_logInfo.ExcuteStartTime = DateTime.Now;
//获取request.Body内容
if (request.Method.ToLower ().Equals ("post")) {
request.EnableRewind (); //启用倒带功能,就可以让 Request.Body 可以再次读取
Stream stream = request.Body;
byte[] buffer = new byte[request.ContentLength.Value];
stream.Read (buffer, 0, buffer.Length);
_logInfo.RequestBody = Encoding.UTF8.GetString (buffer);
request.Body.Position = 0;
} else if (request.Method.ToLower ().Equals ("get")) {
_logInfo.RequestBody = request.QueryString.Value;
}
//获取Response.Body内容
var originalBodyStream = context.Response.Body;
using (var responseBody = new MemoryStream ()) {
context.Response.Body = responseBody;
await _next (context);
_logInfo.ResponseBody = await FormatResponse (context.Response);
_logInfo.ExcuteEndTime = DateTime.Now;
Log4Net.LogInfo ($"VisitLog: {_logInfo.ToString()}");
await responseBody.CopyToAsync (originalBodyStream);
}
}
private async Task<string> FormatResponse (HttpResponse response) {
response.Body.Seek (0, SeekOrigin.Begin);
var text = await new StreamReader (response.Body).ReadToEndAsync ();
response.Body.Seek (0, SeekOrigin.Begin);
return text;
}
}
public static class RequestResponseLoggingMiddlewareExtensions {
public static IApplicationBuilder UseRequestResponseLogging (this IApplicationBuilder builder) {
return builder.UseMiddleware<RequestResponseLoggingMiddleware> ();
}
}
}
把中间件添加到管道中 Pipeline
在 Startup.cs 添加
public void Configure (IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
if (env.IsDevelopment ()) {
app.UseDeveloperExceptionPage ();
} else {
app.UseHsts ();
}
loggerFactory.AddLog4Net ();
app.UseRequestResponseLogging();
// app.UseHttpsRedirection();
app.UseMvc ();
}
来源:oschina
链接:https://my.oschina.net/u/4300922/blog/3519810