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 ();
        }

 

 

posted @ 2019-05-29 15:24  OhMyJie  阅读(2398)  评论(7编辑  收藏