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

Wesley13
• 阅读 470

前提:

  需要nuget   Microsoft.Extensions.Logging.Log4Net.AspNetCore   2.2.6;

        Swashbuckle.AspNetCore 我暂时用的是  4.01;

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

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

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

解决办法:

  步骤1  配置 Swagger 接口文档

    对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管道增加 SwaggerUi

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

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

/// <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

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

/// <summary>
    /// 定义可读Body的接口
    /// </summary>
    public interface IReadableBody
    {
        /// <summary>
        /// 获取或设置是否可读
        /// </summary>
        bool IsRead { get; set; }

        /// <summary>
        /// 读取文本内容
        /// </summary>
        /// <returns></returns>
        Task<string> ReadAsStringAsync();
    }

IReadableBody

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

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

   步骤5  增加Api 过滤器 ApiFilterAttribute

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

/// <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

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这