RecyclableMemoryStreamManager
RecyclableMemoryStreamManager
起因:
專案有一個需求,是要撰寫ApiLog。
由於是要攔截所有Api的Request & Respons,所以最後決定寫成Middleware(中介軟體)。
撞到鬼(遇到的問題):
寫到一半,發現我無論如何怎麼寫,只要在解析API的Request 或 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
至此,順利的把Api的Request & Response 解析及存放起來。
可以安心下班,回家洗洗睡去。