原文:ASP.NET Core 如何记录每次响应的Response信息 - sky 胡萝卜星星 - CSDN博客

上一篇文章中我们已经成功的记录了Request部分的信息,现在我们来看下如何记录Response的内容。

相比于Request,Response额外多了个StatusCode,然后内容都是通过Body读取,不过不同于Request.Body的只读,Response.Body是个只写的数据流。

可以看到默认Response.Body数据流数据类型为Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream。通过查看其源代码,我们可以看到该数据流其实是个空壳,其仅仅是IHttpResponseControl的外部封装,因为前面已经有了替换Request.Body的经验,所以此处理所当然的,我们也应该将默认的Response.Body替换成一个可以读取的输出流。

为了保证通用,我们肯定不能按照HttpResponseStream的做法来重写一个可以读取并且可以重新设置位置的Stream,所以此处思路上直接简单的考虑对MemoryStream进行封装,其内部包含默认的HttpResponseStream,这样不管未来实际Response.Body究竟是什么,我们都可以做到从中读取并恢复数据流当前位置,保证程序的正确运行。

通过查看HttpResponseStream源代码,我们已经可以知道其核心是WriteFlush两个方法,既然是打算对MemoryStream进行封装,那么我们也需要了解下其源代码。通过源码,我们可以发现其BeginWriteWriteByteWriteAsync等方法底层其实都是在调用Write方法,再加上必需的DisposeFlush,所以最终我们得到如下代码

    public class MemoryWrappedHttpResponseStream : MemoryStream
{
private Stream _innerStream;
public MemoryWrappedHttpResponseStream(Stream innerStream)
{
this._innerStream = innerStream ?? throw new ArgumentNullException(nameof(innerStream));
}
public override void Flush()
{
this._innerStream.Flush();
base.Flush();
} public override void Write(byte[] buffer, int offset, int count)
{
base.Write(buffer, offset, count);
this._innerStream.Write(buffer, offset, count);
} protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
this._innerStream.Dispose();
}
} public override void Close()
{
base.Close();
this._innerStream.Close();
}
}

然后我们将上篇内容中读取数据流的代码稍作调整

        private async Task<string> ReadBodyAsync(HttpResponse response)
{
if (response.Body.Length > 0)
{
//var position = response.Body.Position;
response.Body.Seek(0, SeekOrigin.Begin);
var encoding = this.GetEncoding(response.ContentType);
var retStr = await ReadStreamAsync(response.Body, encoding, false).ConfigureAwait(false);
//response.Body.Position = position;
//读取完成后再重新赋值位置这个过程可能不需要,因为数据流是只写的
return retStr;
}
return null;
} private Encoding GetEncoding(string contentType)
{
var mediaType = contentType == null ? default(MediaType) : new MediaType(contentType);
var encoding = mediaType.Encoding;
if (encoding == null)
{
encoding = Encoding.UTF8;
}
return encoding;
} private void EnableReadAsync(HttpResponse response)
{
if (!response.Body.CanRead || !response.Body.CanSeek)
{
response.Body = new MemoryWrappedHttpResponseStream(response.Body);
}
} private async Task<string> ReadStreamAsync(Stream stream, Encoding encoding,bool forceSeekBeginZero=true)
{
using (StreamReader sr = new StreamReader(stream, encoding, true, 1024, true))//这里注意Body部分不能随StreamReader一起释放
{
var str = await sr.ReadToEndAsync();
if (forceSeekBeginZero)
{
stream.Seek(0, SeekOrigin.Begin);//内容读取完成后需要将当前位置初始化,否则后面的InputFormatter会无法读取
}
return str;
}
}

这样如何读取Body部分的准备工作就做完了,下面我们要做的就是在中间件的InvokeAsyncInvoke方法中声明相关的调用,其大致代码如下

        public async Task InvokeAsync(HttpContext context)
{
this.EnableReadAsync(context.Response); context.Response.OnCompleted(async o =>
{
var c = o as HttpContext;
if (c != null)
{
var retStr = await this.ReadBodyAsync(c.Response).ConfigureAwait(false);
//this._logger.LogDebug("Response at:{Time} Response:{Response}", DateTime.Now, retStr);
}
}, context);
await _next(context);
}

上面已经完成了如何在中间件中读取Response.Body,但实际不可能所有的响应都要做记录,另外上面的方法其实额外消耗了服务器内存(而且是大量),所以我们应该还要研究下如何筛选哪些请求需要记录,然后在此基础上再确认哪些响应内容应当被记录,这些将在下次研究完成后再述。

ASP.NET Core 如何记录每次响应的Response信息 - sky 胡萝卜星星 - CSDN博客的更多相关文章

  1. ASP.NET Core 如何记录每次请求的Request信息 - sky 胡萝卜星星 - CSDN博客

    原文:ASP.NET Core 如何记录每次请求的Request信息 - sky 胡萝卜星星 - CSDN博客 版权声明:本文为starfd原创文章,转载请标明出处. https://blog.csd ...

  2. ASP.NET Core默认注入方式下如何注入多个实现(多种方式) - sky 胡萝卜星星 - CSDN博客

    原文:ASP.NET Core默认注入方式下如何注入多个实现(多种方式) - sky 胡萝卜星星 - CSDN博客 版权声明:本文为starfd原创文章,转载请标明出处. https://blog.c ...

  3. asp.net core2.0 依赖注入 AddTransient与AddScoped的区别 - 晓剑 - CSDN博客

    原文:asp.net core2.0 依赖注入 AddTransient与AddScoped的区别 - 晓剑 - CSDN博客 原文地址:http://www.tnblog.net/aojiancc2 ...

  4. ASP.NET Core 入门教程 10、ASP.NET Core 日志记录(NLog)入门

    一.前言 1.本教程主要内容 ASP.NET Core + 内置日志组件记录控制台日志 ASP.NET Core + NLog 按天记录本地日志 ASP.NET Core + NLog 将日志按自定义 ...

  5. ASP.NET Core 实战:使用 NLog 将日志信息记录到 MongoDB

    一.前言 在项目开发中,日志系统是系统的一个重要组成模块,通过在程序中记录运行日志.错误日志,可以让我们对于系统的运行情况做到很好的掌控.同时,收集日志不仅仅可以用于诊断排查错误,由于日志同样也是大量 ...

  6. ASP.NET Web API 记录请求响应数据到日志的一个方法

    原文:http://blog.bossma.cn/dotnet/asp-net-web-api-log-request-response/ ASP.NET Web API 记录请求响应数据到日志的一个 ...

  7. ASP.NET Core错误处理中间件[1]: 呈现错误信息

    NuGet包"Microsoft.AspNetCore.Diagnostics"中提供了几个与异常处理相关的中间件.当ASP.NET Core应用在处理请求过程中出现错误时,我们可 ...

  8. Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客

    ==他的博客应该不错,没有细看 Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客 http://blog.csdn.net/u012706811/article/det ...

  9. asp.net core过滤器记录响应对象

    百度到的基本上就是读取response.body的流.然后记录完了之后,把流的index重新复位,这样也太麻烦了. 其实asp.net core团队肯定已经考虑到了这种需求,比如记录请求响应日志.给响 ...

随机推荐

  1. 牛客网NOIP赛前集训营-提高组(第三场)A 管道维修

    https://www.nowcoder.com/acm/contest/174/A 这个的话  一个位置被清理的时间就是它到空白格子/边界的最短路对吧qww 然后求期望的话 假设它在第i步被清理掉的 ...

  2. Java--返回类的对象(return this)

    如下代码所示: public Book getBook(){ return this; } 在getBook()方法中,方法的返回值为Book类,所以方法体中使用 return this 这种形式返回 ...

  3. docker的网络(进阶)

    overlay网络 overlay网络驱动程序会在多个docker守护程序(即多个主机上的docker守护程序)之间创建分布式网络.该网络(overlays)位于特定于主机的网络之上,允许连接到它的容 ...

  4. 常用模块之configpaser与shutil

    configparser模块 定义:configparser翻译为配置解析,即它是用来解析配置文件的 配置文件:用于编写程序的配置信息的文件 配置文件编写格式 配置文件中只允许出现两种类型的数据 se ...

  5. django第五天(虚拟环境安装和视图层相关)

    django第5天 虚拟环境安装 ''' 1.通过pip3安装虚拟环境: -- pip3 install virtualenv 2.前往目标文件夹: -- cd 目标文件夹 (C:\Virtualen ...

  6. return_url notify_url

    return_url对返回订单状态进行更新和判断,notify_url为异步调动页面,需要先判断notify_url里是否对订单数据做过处理,避免重复更新数据,然后如果用户付款成功直接关闭页面,会造成 ...

  7. performSelectorOnMainThread

    在多线程操作中,有一个著名的错误,叫做"Tried to obtain the web lock from a thread other than the main thread or th ...

  8. 大数据学习——sql练习

    1. 现有如下的建表语句和数据: 建表语句 create table student(Sno int,Sname string,Sex string,Sage int,Sdept string)row ...

  9. 【Luogu】P1312Mayan游戏(暴搜)

    题目链接 由于是暴搜题,所以这篇博客只讲怎么优化剪枝,以及一些细节. 模拟消除思路:因为消除可以拆分成小的横条或竖条,而这些条的长度至少为三,所以一块可消除的区域至少会有一个中心点.这里的中心点可以不 ...

  10. BZOJ 3282 Tree ——Link-Cut Tree

    [题目分析] 明显的LCT维护连通性的题目. access的操作是比较巧妙的,可以把结点到根变成偏爱路径,而且保证了该点是链上深度最深的点. 而且需边的思想也很巧妙,保证了复杂度. 但是只能用于修改路 ...