一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - ASP.NET教程 - ASP.NET Core自定義中間件如何讀取Request.Body與Response.Body的內容詳解

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

2020-07-01 14:54Lamond Lu ASP.NET教程

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

背景#

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

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

原始代碼#

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

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

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

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

根據以上思路,我編寫了以下代碼。

LoggerMiddleware.cs

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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不可讀#

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

?
1
System.ArgumentException: Stream was not readable.

即ASP.NET Core默認創建的Response.Body屬性是不可讀的。

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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關閉,導致后續由于Stream關閉,而不能再次讀取Stream中的內容。如果必須使用,請使用StreamReader的以下重載,將leaveOpen參數設置為true, 確保StreamReader對象被銷毀的時候不會自動關閉讀取的Stream.
?
1
public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen);

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

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

進一步完善代碼#

以上代碼實現,看似已經能夠讀取Response.Body的內容了,但是其實還是有問題的。

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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中間件的任務完成之后,我們需要將后續產生的Response.Body流追加到原始Stream中,然后將Response.Body對象重置為這個新的Stream。

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

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

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

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

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

?
1
A non-empty request body is required.

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

?
1
2
3
4
5
6
7
8
9
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指針重置

?
1
2
3
4
5
6
7
8
9
10
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之后,你會得到以下結果。

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

?
1
System.NotSupportedException: Specified method is not supported.

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

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

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

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

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

下面我們修改代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/lwqlun/p/10954936.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 白丝校花被扒开双腿喷水小说 | 喜欢老头吃我奶躁我的动图 | 公交车揉捏大乳呻吟喘娇 | 91制片厂制作传媒网站破解 | 性趣味商品推荐 | 村上里沙40分钟在线观看 | 成在线人免费 | 国产精品密播放国产免费看 | 星空无限传媒xk8046 | 日韩亚洲国产激情在线观看 | 久久青草费线频观看国产 | 国产99视频精品免视看7 | 成人综合网站 | 日本孕妇与黑人xxxxxx | 喷潮女王cytherea全部视频 | 亚洲人成影院午夜网站 | 日本视频在线观看播放 | 91精品国产亚一区二区三区 | 日韩伦理在线免费观看 | 奇米色7777 | 亚州成人 | 大陆男同志gayxxx | 精品综合久久久久久8888 | 精品精品国产自在现拍 | 欧美一区a | 天天操精品视频 | 男人把大ji巴放进男人免费视频 | 国人精品视频在线观看 | 国产成人理在线观看视频 | 玩高中女同桌肉色短丝袜脚文 | 午夜亚洲福利 | 12一14性xxxxx国外 | 免费看打屁股视频的软件 | 国产专区日韩精品欧美色 | 九九热这里只有精品视频免费 | 亚洲欧美国产另类 | 亚洲va精品中文字幕 | 幸福草电视剧演员表介绍 | 日产精品卡一卡2卡三卡乱码工厂 | 热99在线观看| 亚洲天堂2016|