如何在 ASP.NET Core 中间件中直接将响应正文设置为文件流?
Posted
技术标签:
【中文标题】如何在 ASP.NET Core 中间件中直接将响应正文设置为文件流?【英文标题】:How to directly set response body to a file stream in ASP.NET Core middleware? 【发布时间】:2020-02-27 09:59:44 【问题描述】:以下用于在 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
是对 initialized before 对象 (HttpResponseStream
) 的引用,它在 HttpContext
中可用。假设所有字节都写入这个原始流。如果您将Body
更改为引用(指向)context.Response.Body = a_new_Stream
的新流对象,则原始Stream
根本不会更改。
另外,如果您查看ASP.NET Core
的源代码,您会发现团队总是将包装流复制到最后的原始正文流,而不是简单地替换(除非他们使用模拟流进行单元测试)。比如SPA Prerendering middleware源代码:
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);
作为一种解决方法,您可以将字节复制到原始流,如下所示:
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;
【讨论】:
感谢您的解释。对于文件内容响应,我认为httpContext.Response.SendFileAsync
也可以,完全不需要FileStream
。
@Nick 是的,当然。上面的CopyToAsync
只是说明如何处理普通Stream
的一种方式。如果您正在处理文件,请随意使用更简单的方法:)【参考方案2】:
问题中的using
语句会导致您的流和流阅读器相当短暂,因此它们都将被释放。 “body”中对蒸汽的额外引用不会阻止处理。
框架在发送响应后处理流。 (媒介就是信息)。
【讨论】:
以上是关于如何在 ASP.NET Core 中间件中直接将响应正文设置为文件流?的主要内容,如果未能解决你的问题,请参考以下文章
Error Handling in ASP.NET Core
ASP.NET Core Web API - 如何在中间件管道中隐藏 DbContext 事务?
ASP.Net Core 2.0 如何获取中间件中的所有请求标头? [复制]