ASP.NET Core 入门(2)(WebApi接口请求日志 Request和Response)
以前 .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 (); }