.Net Core Middleware MemoryStreamManager

RecyclableMemoryStreamManager

施明竹 2019/12/31 11:57:09
142

RecyclableMemoryStreamManager

 

起因:

專案有一個需求,是要撰寫ApiLog

由於是要攔截所有ApiRequest & Respons,所以最後決定寫成Middleware(中介軟體)

 

撞到鬼(遇到的問題)

寫到一半,發現我無論如何怎麼寫,只要在解析APIRequest Response時,都會產生一個錯誤訊息:System Information Leak

 

去查的結果,都是記憶體流失相關的。

從早上九點,一直到晚上六七點,一直在鬼打牆。

 

最後,在某篇技術文章的回覆中,看到了RecyclableMemoryStreamManager 這個套件。

再從RecyclableMemoryStreamManager的相關技術文章中,去找到某位pg的寫法。

雖然不是最佳的程式碼,但至少不會讓我再一直撞到【System Information Leak】這隻鬼。

 

所以,最後ApiLogMiddleware的關鍵程式碼如下:

public async Task Invoke(HttpContext context, ILogService logService)

{

this._recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();

this.sw = new Stopwatch();

 

using (MemoryStream requestBodyStream = _recyclableMemoryStreamManager.GetStream())

{

using (MemoryStream responseBodyStream = _recyclableMemoryStreamManager.GetStream())

{

Stream originalRequestBody = context.Request.Body;

context.Request.EnableRewind();

Stream originalResponseBody = context.Response.Body;

 

try

{

await context.Request.Body.CopyToAsync(requestBodyStream);

requestBodyStream.Seek(0, SeekOrigin.Begin);

 

string request = new StreamReader(requestBodyStream).ReadToEnd();

 

requestBodyStream.Seek(0, SeekOrigin.Begin);

context.Request.Body = requestBodyStream;

 

string response = "";

 

context.Response.Body = responseBodyStream;

 

this.sw.Start();

await _next(context);

this.sw.Stop();

 

responseBodyStream.Seek(0, SeekOrigin.Begin);

response = new StreamReader(responseBodyStream).ReadToEnd();

 

await logService.LogReqRespAsync(

context, (int)this.sw.ElapsedMilliseconds, request, response);

 

responseBodyStream.Seek(0, SeekOrigin.Begin);

 

await responseBodyStream.CopyToAsync(originalResponseBody);

}

catch (Exception ex)

{

byte[] data = Encoding.UTF8.GetBytes("Unhandled Error occured. Please, try again in a while.Exception Message:" + ex.ToString());

originalResponseBody.Write(data, 0, data.Length);

}

finally

{

context.Request.Body = originalRequestBody;

context.Response.Body = originalResponseBody;

}

}

}

 

}

 

程式的主要關鍵處,在於二層的using

using (MemoryStream requestBodyStream = _recyclableMemoryStreamManager.GetStream())

{

    using (MemoryStream responseBodyStream = _recyclableMemoryStreamManager.GetStream())

 

第一層用來解析及存放requestBodyStream

第二層用來解析及存放responseBodyStream

 

至此,順利的把ApiRequest & Response 解析及存放起來。

可以安心下班,回家洗洗睡去。

施明竹