最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

ASP.NET Core自定義中間件如何讀取Request.Body與Response.Body的內容詳解

 更新時間:2020年05月25日 09:00:37   作者:Lamond Lu  
這篇文章主要給大家介紹了關于在ASP.NET Core自定義中間件中如何讀取Request.Body與Response.Body的內容,文中通過示例代碼介紹的非常詳細,對大家學習或者使用ASP.NET Core具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧

背景#

最近在徒手造輪子,編寫一個ASP.NET Core的日志監(jiān)控器,其中用到了自定義中間件讀取Request.Body和Response.Body的內容,但是編寫過程,并不像想象中的一帆風順,ASP.NET Core針對Request.Body和Response.Body的幾個特殊設計,導致了完成以上功能需要繞一些彎路。

 

原始代碼#

為了讀取Request.Body和Response.Body的內容,我的實現(xiàn)思路如下:

創(chuàng)建一個LoggerMiddleware的中間件,將它放置在項目中間件管道的頭部。因為根據(jù)ASP.NET Core的中間件管道設計,只有第一個中間件才能獲取到原始的請求信息和最終的響應信息。

Request.Body和Response.Body屬性都是Steram類型, 在LoggerMiddleware中間件的InvokeAsync方法中,我們可以分別使用StreamReader讀取Request.Body和Response.Body的內容。

根據(jù)以上思路,我編寫了以下代碼。

LoggerMiddleware.cs

	public class LoggerMiddleware
 {
 private readonly RequestDelegate _next;

 public LoggerMiddleware(RequestDelegate next)
 {
  _next = next;
 }

 public async Task InvokeAsync(HttpContext context)
 {
  var requestReader = new StreamReader(context.Request.Body);

 		var requestContent = requestReader.ReadToEnd();
 		Console.WriteLine($"Request Body: {requestContent}");

 		await _next(context);

 		var responseReader = new StreamReader(context.Response.Body);
 		var responseContent = responseReader.ReadToEnd();
 		Console.WriteLine($"Response Body: {responseContent}");
 }
 }

Startup.cs

 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 {
 if (env.IsDevelopment())
 {
  app.UseMiddleware<LoggerMiddleware>();
  app.UseDeveloperExceptionPage();
 }
 else
 {
  app.UseHsts();
 }

 app.UseHttpsRedirection();
 app.UseMvc();
 }

問題1:Response.Body的Stream不可讀#

這里為了測試我創(chuàng)建了一個默認的ASP.NET Core WebApi項目。當運行程序,使用GET方式調用/api/values之后,控制臺會返回第一個需要處理的錯誤。

System.ArgumentException: Stream was not readable.

即ASP.NET Core默認創(chuàng)建的Response.Body屬性是不可讀的。

這一點我們可以通過打斷點看到Response.Body屬性的CanRead值是false。

這就很糟糕了,ASP.NET Core默認并不想讓我們在中間件中直接讀取Response.Body中的信息。

這里看似的無解,但是我們可以轉換一下思路,既然ASP.NET Core默認將Response.Body是不可讀的,那么我們就使用一個可讀可寫的Stream對象將其替換掉。這樣當所有中間件都依次執(zhí)行完之后,我們就可以讀取Response.Body的內容了。

public async Task InvokeAsync(HttpContext context)
{
	 var requestReader = new StreamReader(context.Request.Body);

 var requestContent = requestReader.ReadToEnd();
 Console.WriteLine($"Request Body: {requestContent}");

 using (var ms = new MemoryStream())
 {
  context.Response.Body = ms;
  await _next(context);

  context.Response.Body.Position = 0;

  var responseReader = new StreamReader(context.Response.Body);

  var responseContent = responseReader.ReadToEnd();
  Console.WriteLine($"Response Body: {responseContent}");

  context.Response.Body.Position = 0;
 }
}

注意:

  • 讀取Response.Body的時候,需要設置Position = 0, 這樣是為了重置指針,如果不這樣做的話,會導致讀取的流不正確。
  • 這里千萬不要用using包裹StreamReader, 因為StreamReader會在讀取完Stream內容之后,將Stream關閉,導致后續(xù)由于Stream關閉,而不能再次讀取Stream中的內容。如果必須使用,請使用StreamReader的以下重載,將leaveOpen參數(shù)設置為true, 確保StreamReader對象被銷毀的時候不會自動關閉讀取的Stream.
public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen);

重新啟動程序,請求/api/values, 我們就得到的正確的結果。

進一步完善代碼#

以上代碼實現(xiàn),看似已經(jīng)能夠讀取Response.Body的內容了,但是其實還是有問題的。

回想一下,我們做出以上方案的前提是,當前LoggerMiddleware中間件必須位于中間件管道的頭部。

如果不能保證這個約定, 就會出現(xiàn)問題,因為我們在LoggerMiddleware中間件中將Response.Body屬性指向了一個新的可讀可寫的Stream對象。如果LoggerMiddleware中間件之前的某個中間件中設置過Response.Body, 就會導致這部分設置丟失。

因此正確的設置方式應該是這樣的:

 public async Task InvokeAsync(HttpContext context)
 {
 var originalResponseStream = context.Response.Body;

 var requestReader = new StreamReader(context.Request.Body);
 
 var requestContent = requestReader.ReadToEnd();
 Console.WriteLine($"Request Body: {requestContent}");
 

 using (var ms = new MemoryStream())
 {
  context.Response.Body = ms;
  await _next(context);


  ms.Position = 0;
  var responseReader = new StreamReader(ms);

  var responseContent = responseReader.ReadToEnd();
  Console.WriteLine($"Response Body: {responseContent}");

  ms.Position = 0;

  await ms.CopyToAsync(originalResponseStream);
  context.Response.Body = originalResponseStream;
 }
 }

代碼解釋:

  • 這里當進入LoggerMiddleware中間件時,我們將之前中間件操作完成之后的Response.Body對象對應的原始Stream, 保存在一個臨時變量中
  • 當LoggerMiddelware中間件的任務完成之后,我們需要將后續(xù)產(chǎn)生的Response.Body流追加到原始Stream中,然后將Response.Body對象重置為這個新的Stream。

至此Repsonse.Body的問題都解決,下面我們再來看一下Request.Body的問題。

問題2:Request.Body的內容可以正確的顯示,但是后續(xù)的ModelBinding都失敗了#

下面我們來請求POST /api/values, Request.Body里面的內容是字符串"123123"

 

服務器端返回了400錯誤, 錯誤信息

A non-empty request body is required.

這里就很奇怪,為啥請求體是空呢?我們回到中間件部分代碼,這里我們在讀取完Request.Body中的Stream之后,沒有將Stream的指針重置,當前指針已經(jīng)是Stream的尾部,所以后續(xù)ModelBinding的時候,讀取不到Stream的內容了。

 public async Task InvokeAsync(HttpContext context)
 {
 ...
 var requestReader = new StreamReader(context.Request.Body);
 
 var requestContent = requestReader.ReadToEnd();
 Console.WriteLine($"Request Body: {requestContent}");
 ...
 }

于是,這里我們需要采取和Response.Body相同的處理方式,在讀取完Request.Body之后,我們需要將Request.Body的Stream指針重置

 public async Task InvokeAsync(HttpContext context)
 {
 ...
 var requestReader = new StreamReader(context.Request.Body);
 
 var requestContent = requestReader.ReadToEnd();
 Console.WriteLine($"Request Body: {requestContent}");
 context.Request.Body.Position = 0;
 ...
 }

你一定覺著至此問題就解決了,不過ASP.NET Core和你又開了一個玩笑。

當你重新請求POST /api/values之后,你會得到以下結果。

錯誤原因:

System.NotSupportedException: Specified method is not supported.

翻譯過來就是指定方法不支持。到底不支持啥呢?在代碼上打上斷點,你會發(fā)現(xiàn)Request.Body的CanSeek屬性是false, 即Request.Body的Stream, 你是不能隨便移動指針的,只能按順序讀取一次,默認不支持反復讀取。

那么如何解決這個問題呢?

你可以在使用Request對象中的EnableRewind或者EnableBuffering。 這2個方法的作用都是在內存中創(chuàng)建緩沖區(qū)存放Request.Body的內容,從而允許反復讀取Request.Body的Stream。

說明: 其實EnableBuffering方法內部就只直接調用的EnableRewind方法。

下面我們修改代碼

 public async Task InvokeAsync(HttpContext context)
 {
 context.Request.EnableBuffering();
 var requestReader = new StreamReader(context.Request.Body);

 var requestContent = requestReader.ReadToEnd();
 Console.WriteLine($"Request Body: {requestContent}");
 context.Request.Body.Position = 0;


 using (var ms = new MemoryStream())
 {
  context.Response.Body = ms;
  await _next(context);


  ms.Position = 0;
  var responseReader = new StreamReader(ms);

  var responseContent = responseReader.ReadToEnd();
  Console.WriteLine($"Response Body: {responseContent}");

  ms.Position = 0;
 }
 }

再次請求POST /api/values, api請求被正確的處理了。

源代碼: https://github.com/lamondlu/webapi-logger

總結

到此這篇關于ASP.NET Core自定義中間件中如何讀取Request.Body與Response.Body的內容的文章就介紹到這了,更多相關ASP.NET Core自定義中間件讀取Request.Body與Response.Body內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • ASP.NET的實用技巧詳細介紹

    ASP.NET的實用技巧詳細介紹

    本文介紹的是ASP.NET的實用技巧,從跟蹤頁面,表單數(shù)據(jù)的服務器端驗證和跳過表單驗證等方面為大家介紹的。希望對你有幫助,一起來看。
    2015-10-10
  • 淺談AjaxPro.dll,asp.net 前臺js調用后臺方法

    淺談AjaxPro.dll,asp.net 前臺js調用后臺方法

    這篇文章主要介紹了淺談AjaxPro.dll,asp.net 前臺js調用后臺方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • 聊聊.Net,Core配置Nlog.md的問題

    聊聊.Net,Core配置Nlog.md的問題

    這篇文章主要介紹了.Net?Coer配置Nlog.md的相關知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • asp.net core 實現(xiàn)一個簡單的倉儲的方法

    asp.net core 實現(xiàn)一個簡單的倉儲的方法

    本篇文章主要介紹了asp.net core 實現(xiàn)一個簡單的倉儲的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • asp.net 讀取并顯示excel數(shù)據(jù)的實現(xiàn)代碼

    asp.net 讀取并顯示excel數(shù)據(jù)的實現(xiàn)代碼

    Microsoft Office Excel是一個很好的電子表格應用程序,在本文中,it同學會將教給你看到如何使用ASP.NET從Excel電子表格讀取并顯示顯示數(shù)據(jù)。
    2010-02-02
  • asp.net Gridview行綁定事件新體會

    asp.net Gridview行綁定事件新體會

    最近在做統(tǒng)計GridView中某一列的加總值,之前有在RowDataBound事件中中逐筆加總,經(jīng)過測試才知道,這是不正確的,并不能得到所有資料的加總值。
    2009-11-11
  • aspx與ascx,ashx的用法總結

    aspx與ascx,ashx的用法總結

    這篇文章主要是對aspx與ascx,ashx的用法進行了詳細的總結介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2013-12-12
  • asp.net關于Cookie跨域(域名)的問題

    asp.net關于Cookie跨域(域名)的問題

    Cookie是一個偉大的發(fā)明,它允許Web開發(fā)者保留他們的用戶的登錄狀態(tài)。但是當你的站點有一個以上的域名時就會出現(xiàn)問題了。在Cookie規(guī)范上說,一個cookie只能用于一個域名,不能夠發(fā)給其它的域名。因此,如果在瀏覽器中對一個域名設置了一個cookie,這個cookie對于其它的域名將無效。如果你想讓你的用戶從你的站點中的其中一個進行登錄,同時也可以在其它域名上進行登錄,這可真是一個大難題。
    2012-12-12
  • Grid或者DataTable中數(shù)據(jù)導出為Excel原來這么簡單

    Grid或者DataTable中數(shù)據(jù)導出為Excel原來這么簡單

    以前一直認為,將Grid 或者DataTable中的數(shù)據(jù)導出到Excel功能實現(xiàn)會非常復雜,可能會想用什么類庫什么的或者實在太難就用csv算了,沒想到真的很簡單,需要了解的朋友可以參考下
    2012-12-12
  • 無法將類型為“System.Web.UI.WebControls.HiddenField”的對象強制轉換為類型的錯誤的解決

    無法將類型為“System.Web.UI.WebControls.HiddenField”的對象強制轉換為類型的錯誤的解

    無法將類型為“System.Web.UI.WebControls.HiddenField”的對象強制轉換為類型的錯誤的解決方法
    2011-12-12

最新評論

九江市| 韩城市| 防城港市| 刚察县| 石渠县| 宁明县| 万州区| 宜城市| 肇源县| 稷山县| 富川| 江都市| 庐江县| 定州市| 娱乐| 溆浦县| 吉林省| 武功县| 丹巴县| 九江县| 佛坪县| 江西省| 商水县| 沙河市| 乐平市| 青龙| 临武县| 澎湖县| 平果县| 新竹县| 丹阳市| 兴山县| 凤翔县| 二连浩特市| 临城县| 南通市| 蒙阴县| 乌鲁木齐市| 防城港市| 宁南县| 香河县|