.NET CORE之API日志收集

我们在构建WEBAPI项目时,通常需要构建一个全局的记录API 请求和返回 的功能,在WEBAPI框架下 我们通过自定义一个DelegateHandler来实现这个功能,

在.NET CORE框架下已经不存在DelegateHandler管道了,我们需要通过Middleware管道来实现。具体实现如下:

定义LoggingMiddleware

    public class GlobalApiLoggingMiddleware : IMiddleware
    {
        private readonly ILogger _logger;

        public GlobalApiLoggingMiddleware(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger("ApiLog");
        }
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
        //在这里我们来拦截请求,收集日志
 await next.Invoke(context);
}
}

HttpContext的定义

可以看到里面的Request和Response对象 分别时 HttpRequest 和 HttpResponse,不再像在webapi框架下直接通过HttpRequestMessage、HttpResponseMessage来获取请求报文和返回报文。

这里需要花费一些技巧。

获取请求报文,

            //reuqest支持buff,否则body只能读取一次
            context.Request.EnableBuffering();
            //这里不要释放stream,否则后续读取request.body会报错
            var reader = new StreamReader(context.Request.Body, Encoding.UTF8);
            var requestStr = reader.ReadToEnd();

首先类似于webapi框架下获取请求报文一样,需要先设置request buffer,这样request报文可以读取多次

其次获取报文的方式 是通过 stream获取,这里stream不要释放 不要释放 不要释放。重要的事情说三次。

获取返回报文 更加复杂一点

           Stream originalBody = context.Response.Body;

            try
            {
                using (var memStream = new MemoryStream())
                {
                    context.Response.Body = memStream;
                    await next.Invoke(context);

                    var request = context.Request;
                    var log = new ApiLogEntity()
                    {
                        Appkey = request.GetAppkey(),
                        ClientIp = request.GetClientRealIp(),
                        HttpMethod = request.Method,
                        Request = requestStr,
                        RequestId = request.GetRequestId(),
                        RequestUrl = request.Path.Value,
                        QueryString = request.QueryString.Value,
                        ServerIp = request.Host.Value,
                        StatusCode = context.Response.StatusCode
                    };

                    memStream.Position = 0;
                    log.Response = new StreamReader(memStream).ReadToEnd();

                    memStream.Position = 0;
                    await memStream.CopyToAsync(originalBody);

                    _logger.LogInformation(JsonConvert.SerializeObject(log));
                }
            }
            finally
            {
                //重新给response.body赋值,用于返回
                context.Response.Body = originalBody;
            }

这里HttpResponse的body是不允许读取的!!所以这里的策略是 先给body赋值一个新的stream,

执行完action得到返回值后,可以读取我们自己定义的stream拿到返回报文

最后把返回值stream copy给原来的body对象,并重新赋给context.Response.Body,这里客户端可以正确返回了。

OK,结束!

猜你喜欢

转载自www.cnblogs.com/gt1987/p/10981343.html