Asp.net-Core

如何在 ASP.NET Core 中間件中直接將響應主體設置為文件流?

  • May 9, 2022

下面用於在 ASP.NET Core 中間件中寫入文件流的範常式式碼Response.Body不起作用(發出空響應):

public Task Invoke(HttpContext context)
{
   context.Response.ContentType = "text/plain";

   using (var fs = new FileStream("/valid-path-to-file-on-server.txt", FileMode.Open)
   using (var sr = new StreamReader(fs))
   {
       context.Response.Body = sr.BaseStream;
   }

   return Task.CompletedTask;
}

任何想法這種直接設置的方法可能有什麼問題context.Response.Body

注意:管道中的任何下一個中間件都將被跳過,不再進行進一步處理。

更新(另一個例子):一個簡單的MemoryStream分配也不起作用(空響應):

context.Response.Body = new MemoryStream(Encoding.UTF8.GetBytes(DateTime.Now.ToString()));
  1. 不,你永遠不能直接這樣做。

請注意,這context.Response.Body是對對象 ( HttpResponseStream)的引用,該對像在中可用****之前已初始化HttpContext。假設所有字節都寫入這個原始流。如果您將Bodyto 引用(指向)更改為新的流對象,則根本不會更改context.Response.Body = a_new_Stream原始對象。Stream

此外,如果您查看 的原始碼ASP.NET Core,您會發現團隊總是將包裝流複製到最後的原始主體流,而不是進行簡單的替換(除非他們使用模擬流進行單元測試)。例如SPA Prerendering 中間件源碼:

   finally
   {
       context.Response.Body = originalResponseStream;
       ...

ResponseCachingMiddleware原始碼:

   public async Task Invoke(HttpContext httpContext)
   {
       ...
       finally
       {
           UnshimResponseStream(context);
       }
       ...
   }

   internal static void UnshimResponseStream(ResponseCachingContext context)
   {
       // Unshim response stream
       context.HttpContext.Response.Body = context.OriginalResponseStream;

       // Remove IResponseCachingFeature
       RemoveResponseCachingFeature(context.HttpContext);
   }
  1. 作為一種解決方法,您可以將字節複製到原始流,如下所示:
public async Task Invoke(HttpContext context)
{
   context.Response.ContentType = "text/plain";
   using (var fs = new FileStream("valid-path-to-file-on-server.txt", FileMode.Open))
   {
       await fs.CopyToAsync(context.Response.Body);
   }
}

或者,如果您想使用自己的流包裝器劫持原始數據:HttpResponseStream

   var originalBody = HttpContext.Response.Body;
   var ms = new MemoryStream();
   HttpContext.Response.Body = ms;
   try
   {
       await next();
       HttpContext.Response.Body = originalBody;
       ms.Seek(0, SeekOrigin.Begin);
       await ms.CopyToAsync(HttpContext.Response.Body);
   }
   finally
   {
       response.Body = originalBody;
   }

引用自:https://stackoverflow.com/questions/58653564