1. 程式人生 > >ASP.NET Core 如何記錄每次響應的Response資訊

ASP.NET Core 如何記錄每次響應的Response資訊

上一篇文章中我們已經成功的記錄了Request部分的資訊,現在我們來看下如何記錄Response的內容。

相比於Request,Response額外多了個StatusCode,然後內容都是通過Body讀取,不過不同於Request.Body的只讀,Response.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中的響應壓縮

### 介紹     響應壓縮技術是目前Web開發領域中比較常用的技術,在頻寬資源受限的情況下,使用壓縮技術是提升頻寬負載的首選方案。我們熟悉的Web伺服器,比如IIS、Tomcat、Nginx、Apache等都可以使用壓縮技術,常用的壓縮型別包括Brotli、Gzip

ASP.NET Core 如何記錄每次響應Response資訊

上一篇文章中我們已經成功的記錄了Request部分的資訊,現在我們來看下如何記錄Response的內容。 相比於Request,Response額外多了個StatusCode,然後內容都是通過Body讀取,不過不同於Request.Body的只讀,Respons

ASP.NET Core 實戰:使用 NLog 將日誌資訊記錄到 MongoDB

 一、前言   在專案開發中,日誌系統是系統的一個重要組成模組,通過在程式中記錄執行日誌、錯誤日誌,可以讓我們對於系統的執行情況做到很好的掌控。同時,收集日誌不僅僅可以用於診斷排查錯誤,由於日誌同樣也是大量的資料,通過對這些資料進行集中分析,可以產生極大的價值。  在微服務的系統架構中,由於一個系

asp.net core 系列之Performance的 Response compression(響應壓縮)

本文,幫助瞭解響應壓縮的一些知識及用法(大部分翻譯於官網,英文水平有限,不準確之處,歡迎指正)。 什麼是響應壓縮?響應壓縮簡單的說就是為了減少網路頻寬,而把返回的響應壓縮,使之體積縮小,從而加快響應的一種技術(個人理解) 網路頻寬是有限的資源。減少響應(response)的大小通常可以增加應用的響應性(即

ASP.NET Core 的Windows和IIS宿主(自動翻譯記錄)

href imei when webserver cif outside 連接 cor ole https://docs.microsoft.com/en-us/aspnet/core/publishing/iis?tabs=aspnetcore2x 支持的操作系統 以下操

ASP.NET Core】如何隱藏響應頭中的 “Kestrel”

cat 執行 content 裝逼 調用 iap filter col 很好 全宇宙人民都知道,ASP.NET Core 應用是不依賴服務器組件的,因此它可以獨立運行,一般是使用支持跨平臺的 Kestrel 服務器(當然,在 Windows 上還可以考慮用 HttpSys,

Asp.net core實戰6: 新增你的配置資訊

1.依賴注入及服務註冊   微軟也採用了叫做DI依賴注入或稱作IOC控制反轉的東西進行解耦。基本上解決了不同服務之間的過分侵入性設計,通過依賴注入實現不同服務之間的解耦,只關注自己那部分的功能實現,而不是過多的考慮依賴問題。 你需要將你所需使用的Service(簡單地說就是一個

ASP.NET Core使用Elasticsearch記錄NLog日誌

ASP.NET Core使用Elasticsearch記錄NLog日誌 1、新建一個 ASP.NET Core專案   2、安裝Nuge包 執行:Install-Package NLog.Web.AspNetCore 執行:Install-Package NLog 執行:Inst

Asp.net Core 和類庫讀取配置檔案資訊

Asp.net Core 和類庫讀取配置檔案資訊 看乾貨請移步至.net core 讀取配置檔案公共類 首先開一個腦洞,Asp.net core 被使用這麼長時間了,但是關於配置檔案(json)的讀取,微軟官方似乎並沒有給出像.net framework讀取web.config那樣簡單且完美。嚴重懷

ASP.NET Core應用的錯誤處理[4]:StatusCodePagesMiddleware中介軟體如何針對響應碼呈現錯誤頁面

StatusCodePagesMiddleware中介軟體與ExceptionHandlerMiddleware中介軟體比較類似,它們都是在後續請求處理過程中“出錯”的情況下利用一個錯誤處理器來完成最終的請求處理與響應的任務。它們之間的差異在於對“錯誤”的界定上,對於ExceptionHandlerMiddl

學習ASP.NET Core, 怎能不瞭解請求處理管道[3]: 自定義一個伺服器感受一下管道是如何監聽、接收和響應請求的

我們在《伺服器在管道中的“龍頭”地位》中對ASP.NET Core預設提供的具有跨平臺能力的KestrelServer進行了介紹,為了讓讀者朋友們對管道中的伺服器具有更加深刻的認識,接下來我們採用例項演示的形式建立一個自定義的伺服器。這個自定義的伺服器直接利用HttpListener來完成針對請求的監聽、接收

ASP.NET Core應用中如何設定和獲取與執行環境相關的資訊

HostingEnvironment是承載應用當前執行環境的描述,它是對所有實現了IHostingEnvironment介面的所有型別以及對應物件的統稱。如下面的程式碼片段所示,一個HostingEnvironment物件承載的執行環境的描述資訊體現在定義這個介面的6個屬性上。ApplicationName和

ASP.NET Core應用中如何記錄和檢視日誌

日誌記錄不僅對於我們開發的應用,還是對於ASP.NET Core框架功能都是一項非常重要的功能特性。我們知道ASP.NET Core使用的是一個極具擴充套件性的日誌系統,該系統由Logger、LoggerFactory和LoggerProvider這三個核心物件組成。我們可以通過簡單的配置實現對LoggerF

ASP.NET Core 實戰:使用 NLog 將日誌信息記錄到 MongoDB

uil cfg init com shutdown 右鍵 重新 系統 文件的   在項目開發中,日誌系統是系統的一個重要組成模塊,通過在程序中記錄運行日誌、錯誤日誌,可以讓我們對於系統的運行情況做到很好的掌控。同時,收集日誌不僅僅可以用於診斷排查錯誤,由於日誌同樣也是大量的

ASP.NET Core 中文文件 第四章 MVC(2.3)格式化響應資料

ASP.NET Core MVC 內建支援對相應資料(response data)的格式化,用來修正格式或生成客戶端指定的格式。 特定格式的操作結果 某些操作結果(Action result)的型別是指定的特定格式,比如 JsonResult 或 ContentResult。Action 可以返回格式化為

asp.net core 把用戶訪問記錄優化到極致

想象 tro oat gallery blog 系統 定位 初始化 item 菜菜呀,前幾天做的用戶空間,用戶反映有時候比較慢呀CEO,CTO,CFO於一身的CXO是嗎?菜菜我把你拉進用戶反饋群,你解決一下呀CEO,CTO,CFO於一身的CXO(完了,以後沒清凈時候了)我盡

asp.net core 2.1 Mysql 資料庫遷移,遇坑記錄

首先來一段錯誤immodeMacBook-Pro:tz.efcontext immo$ dotnet ef database update Unable to create an object of type 'AppDbContext'. Add an implementa

詳解Asp.Net Core 2.1+的視圖緩存(響應緩存)

直接 -h 代碼 class 參數 img sha ofo get 響應緩存Razor 頁與 ASP.NET 核心 2.0 中不支持。 此功能將支持ASP.NET 核心 2.1 版本。 在老的版本的MVC裏面,有一種可以緩存視圖的特性(OutputCache),可以保

使用 ASP.NET Core MVC 創建 Web API——響應數據的內容協商(七)

found 數據傳輸 tro mep res 實體對象 模型 log conn 使用 ASP.NET Core MVC 創建 Web API 使用 ASP.NET Core MVC 創建 Web API(一) 使用 ASP.NET Core MVC 創建 Web API

使用 ASP.NET Core MVC 建立 Web API——響應資料的內容協商(七)

使用 ASP.NET Core MVC 建立 Web API 使用 ASP.NET Core MVC 建立 Web API(一) 使用 ASP.NET Core MVC 建立 Web API(二)  使用 ASP.NET Core MVC 建立 Web API(三) 使用 ASP.NET C