.net core MVC 经过 Filters 过滤器拦截请求及响应内容

前提:git

  须要nuget   Microsoft.Extensions.Logging.Log4Net.AspNetCore   2.2.6;github

        Swashbuckle.AspNetCore 我暂时用的是  4.01;json

描述:经过 Filters 拦截器获取 Api 请求内容及响应内容,并记录到日志文件;api

 

     有文中代码记录接口每次请求及响应状况以下图:app

 

解决办法:async

  步骤1  配置 Swagger 接口文档ide

    对startup.cs   进行修改代码以下:函数

    ConfigureServices 中增长Swagger 配置测试

services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new Info { Version = "v1", Title = "Filters 过滤器测试Api", Description = @"经过 IActionFilter, IAsyncResourceFilter 拦截器拦截请求及响应上下文并记录到log4日志" }); c.IncludeXmlComments(this.GetType().Assembly.Location.Replace(".dll", ".xml"), true);  //是须要设置 XML 注释文件的完整路径
});

    对 Configure Http管道增长 SwaggerUiui

public void Configure(IApplicationBuilder app, IHostingEnvironment env) {if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); app.UseSwagger(); app.UseSwaggerUI(o => { o.SwaggerEndpoint("/swagger/v1/swagger.json", "Filters 过滤器测试Api"); }); }

   步骤2 建立Log4net 日志帮助类  LogHelper.cs

/// <summary>
    /// 日志帮助类 /// </summary>
    public static class LogHelper { /// <summary>
        /// 日志提供者 /// </summary>
        private static ILogger logger; /// <summary>
        /// 静太方法构造函数 /// </summary>
        static LogHelper() { logger = new LoggerFactory().AddConsole().AddDebug().AddLog4Net().CreateLogger("Logs"); } /// <summary>
        /// 打印提示 /// </summary>
        /// <param name="message">日志内容</param>
        public static void Info(object message) { logger.LogInformation(message?.ToString()); } /// <summary>
        /// 打印错误 /// </summary>
        /// <param name="message">日志内容</param>
        public static void Error(object message) { logger.LogError(message?.ToString()); } /// <summary>
        /// 打印错误 /// </summary>
        /// <param name="ex">异常信息</param>
        /// <param name="message">日志内容</param>
        public static void Error(Exception ex, string message) { logger.LogError(ex, message); } /// <summary>
        /// 调试信息打印 /// </summary>
        /// <param name="message"></param>
        public static void Debug(object message) { logger.LogDebug(message?.ToString()); } }
LogHelper

   步骤3 定义可读写的Http 上下文流接口  IReadableBody.cs 及 http 请求上下文中间件 HttpContextMiddleware.cs

/// <summary>
    /// 定义可读Body的接口 /// </summary>
    public interface IReadableBody { /// <summary>
        /// 获取或设置是否可读 /// </summary>
        bool IsRead { get; set; } /// <summary>
        /// 读取文本内容 /// </summary>
        /// <returns></returns>
        Task<string> ReadAsStringAsync(); }
IReadableBody
/// <summary>
    /// Http 请求中间件 /// </summary>
    public class HttpContextMiddleware { /// <summary>
        /// 处理HTTP请求 /// </summary>
        private readonly RequestDelegate next; /// <summary>
        /// 构造 Http 请求中间件 /// </summary>
        /// <param name="next"></param>
        public HttpContextMiddleware(RequestDelegate next) { this.next = next; } /// <summary>
        /// 执行响应流指向新对象 /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public Task Invoke(HttpContext context) { context.Response.Body = new ReadableResponseBody(context.Response.Body); return this.next.Invoke(context); } /// <summary>
        /// 可读的Response Body /// </summary>
        private class ReadableResponseBody : MemoryStream, IReadableBody { /// <summary>
            /// 流内容 /// </summary>
            private readonly Stream body; /// <summary>
            /// 获取或设置是否可读 /// </summary>
            public bool IsRead { get; set; } /// <summary>
            /// 构造自定义流 /// </summary>
            /// <param name="body"></param>
            public ReadableResponseBody(Stream body) { this.body = body; } /// <summary>
            /// 写入响应流 /// </summary>
            /// <param name="buffer"></param>
            /// <param name="offset"></param>
            /// <param name="count"></param>
            public override void Write(byte[] buffer, int offset, int count) { this.body.Write(buffer, offset, count); if (this.IsRead) { base.Write(buffer, offset, count); } } /// <summary>
            /// 写入响应流 /// </summary>
            /// <param name="source"></param>
            public override void Write(ReadOnlySpan<byte> source) { this.body.Write(source); if (this.IsRead) { base.Write(source); } } /// <summary>
            /// 刷新响应流 /// </summary>
            public override void Flush() { this.body.Flush(); if (this.IsRead) { base.Flush(); } } /// <summary>
            /// 读取响应内容 /// </summary>
            /// <returns></returns>
            public Task<string> ReadAsStringAsync() { if (this.IsRead == false) { throw new NotSupportedException(); } this.Seek(0, SeekOrigin.Begin); using (var reader = new StreamReader(this)) { return reader.ReadToEndAsync(); } } protected override void Dispose(bool disposing) { this.body.Dispose(); base.Dispose(disposing); } } }
HttpContextMiddleware

   步骤4  配置Http管通增长 Http请求上下文中件间

    打开 Startup.cs  ,对管通 Configure 增长以下中间件代码:

    app.UseMiddleware<HttpContextMiddleware>();

   步骤5  增长Api 过滤器 ApiFilterAttribute

/// <summary>
    /// Api 过滤器,记录请求上下文及响应上下文 /// </summary>
    public class ApiFilterAttribute : Attribute, IActionFilter, IAsyncResourceFilter { /// <summary>
        /// 请求Api 资源时 /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) { // 执行前
            try { await next.Invoke(); } catch { } // 执行后
            await OnResourceExecutedAsync(context); } /// <summary>
        /// 记录Http请求上下文 /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task OnResourceExecutedAsync(ResourceExecutingContext context) { var log = new HttpContextMessage { RequestMethod = context.HttpContext.Request.Method, ResponseStatusCode = context.HttpContext.Response.StatusCode, RequestQurey = context.HttpContext.Request.QueryString.ToString(), RequestContextType = context.HttpContext.Request.ContentType, RequestHost = context.HttpContext.Request.Host.ToString(), RequestPath = context.HttpContext.Request.Path, RequestScheme = context.HttpContext.Request.Scheme, RequestLocalIp = (context.HttpContext.Request.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString() + ":" + context.HttpContext.Request.HttpContext.Connection.LocalPort), RequestRemoteIp = (context.HttpContext.Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString() + ":" + context.HttpContext.Request.HttpContext.Connection.RemotePort) }; //获取请求的Body //数据流倒带 context.HttpContext.Request.EnableRewind();
            if (context.HttpContext.Request.Body.CanSeek) { using (var requestSm = context.HttpContext.Request.Body) { requestSm.Position = 0; var reader = new StreamReader(requestSm, Encoding.UTF8); log.RequestBody = reader.ReadToEnd(); } } //将当前 http 响应Body 转换为 IReadableBody
            if (context.HttpContext.Response.Body is IReadableBody body) { if (body.IsRead) { log.ResponseBody = await body.ReadAsStringAsync(); } } if (string.IsNullOrEmpty(log.ResponseBody) == false && log.ResponseBody.Length > 200) { log.ResponseBody = log.ResponseBody.Substring(0, 200) + "......"; } LogHelper.Debug(log); } /// <summary>
        /// Action 执行前 /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuting(ActionExecutingContext context) { //设置 Http请求响应内容设为可读
            if (context.HttpContext.Response.Body is IReadableBody responseBody) { responseBody.IsRead = true; } } /// <summary>
        /// Action 执行后 /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuted(ActionExecutedContext context) { } }
ApiFilterAttribute

  步骤6  对须要记录请求上下文日志的接口加上特性  [ApiFilter]

[ApiFilter] [Route("api/[controller]/[Action]")] [ApiController] public class DemoController : ControllerBase { ....... }

 Demo 地址:https://github.com/intotf/netCore/tree/master/WebFilters

相关文章
相关标签/搜索