我使用app.UseStaticFiles()
在我的应用中提供了大量静态文件。我想在发送之前为特定HTML文件的响应注入一些额外的标记。我的第一次尝试是在静态文件中间件之前添加这样的中间件:
app.Use(async (context, next) => {
await next();
// Modify the response here
});
然而,这不起作用,因为我实际上无法读取读取响应流 - 它使用了引擎盖下的Kestrel FrameResponseStream
,这是不可读的。
所以,我想我可以用MemoryStream
替换响应正文流,我可以写给:
app.Use(async (context, next) => {
context.Response.Body = new MemoryStream();
await next();
// Modify the response here
});
但这只会导致请求永远不会完成 - 它会遍历所有管道阶段,但它甚至不会将任何标头返回给浏览器。
那么,有什么办法可以修改StaticFileMiddleware
生成的响应吗?
更新
由于所讨论的HTML文件很小(765字节),因此不需要考虑内存消耗。但是,任何读取/修改响应的尝试仍会导致与以前相同的问题(不返回任何内容)。更明确地说,这是正在做的事情:
app.Use(async (context, next) => {
var originalStream = context.Response.Body;
var bufferStream = new MemoryStream();
context.Response.Body = bufferStream;
await next();
bufferStream.Seek(0, SeekOrigin.Begin);
if (/* some condition */)
{
var reader = new StreamReader(bufferStream);
var response = await reader.ReadToEndAsync();
// The response string is modified here
var writer = new StreamWriter(originalStream);
await writer.WriteAsync(response);
}
else
{
await bufferStream.CopyToAsync(originalStream);
}
});
符合else
条件的文件返回正常,但if
条件下的特定文件会导致问题。即使我根本不修改流,它仍然会挂起。
答案 0 :(得分:2)
是的,提供的默认流是只读的,因为数据只是暂时缓冲并刷新到客户端。因此,您无法倒带或阅读它。
您的第二次尝试不起作用,因为永远不会处理原始流。您完全用MemoryStream
替换了响应正文流并丢弃了原始请求,因此永远不会写入任何内容并且客户端会永远等待。
你不能忘记,客户端的流是在原始流中,你不能只用其他东西替换它。
致电await next()
后,必须从MemoryStream
读取数据,然后将其写入原始信息流。
app.Use(async (context, next) => {
var originalStream = context.Response.Body;
var memoryStream = new MemoryStream();
context.Response.Body = memoryStream;
await next();
// Here you must read the MemoryStream, modify it, then write the
// result into "originalStream"
});
但请注意,此解决方案会将整个响应缓冲到服务器内存中,因此如果您发送大文件,这将显着降低ASP.NET Core应用程序的性能,尤其是如果您提供几兆字节的文件,并使垃圾收集更频繁地被触发。
这不仅会影响您的静态文件,还会影响您的所有常规请求,因为MVC中间件是在静态文件中间件之后调用的。
如果你真的想在每个请求上修改单个(或文件列表),我宁愿建议你在控制器内执行此操作并在那里路由某些文件。请记住,如果静态文件中间件找不到给定文件,它将调用链中的下一个文件,直到它到达mvc中间件。
只需在那里设置一个匹配特定文件或文件夹的路线,然后将其路由到控制器。读取控制器中的文件并将其写入响应流或只返回新的蒸汽(使用return File(stream, contentType);