简体   繁体   English

在 ASP.NET Core 中修改静态文件响应

[英]Modify static file response in ASP.NET Core

I serve a bunch of static files in my app with app.UseStaticFiles() .我使用app.UseStaticFiles()在我的应用程序中提供了一堆静态文件。 I'd like to inject some additional markup into the response for a particular HTML file before it's sent.我想在发送之前为特定 HTML 文件的响应注入一些额外的标记。 My first attempt was to add middleware like this before the static files middleware:我的第一次尝试是在静态文件中间件之前添加这样的中间件:

app.Use(async (context, next) => {
    await next();

    // Modify the response here
});

However, this doesn't work as I can't actually read the read the response stream - it's using Kestrel's FrameResponseStream under the hood, which is unreadable.但是,这不起作用,因为我实际上无法读取读取响应流 - 它在引擎盖下使用 Kestrel 的FrameResponseStream ,这是不可读的。

So, I figured I could replace the response body stream with a MemoryStream that I could write to:所以,我想我可以用我可以写入的MemoryStream替换响应正文流:

app.Use(async (context, next) => {
    context.Response.Body = new MemoryStream();
    await next();

    // Modify the response here
});

But this just causes the request to never complete - it goes through all the pipeline stages, but it never even returns any headers to the browser.但这只会导致请求永远不会完成 - 它经历了所有管道阶段,但它甚至从未向浏览器返回任何标头。

So, is there any way I can modify the response that is produced by the StaticFileMiddleware ?那么,有什么方法可以修改由StaticFileMiddleware产生的响应?


Update更新

As the HTML file in question is tiny (765 bytes), memory consumption isn't a concern.由于所讨论的 HTML 文件很小(765 字节),因此不必担心内存消耗。 However, any attempts to read/modify the response still causes the same problem as before (nothing is returned).但是,任何读取/修改响应的尝试仍会导致与以前相同的问题(不返回任何内容)。 More explicitly, here's what's being done:更明确地说,这是正在做的事情:

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);
    }
});

The files hitting the else condition are returned just fine, but the particular file in the if condition causes trouble.符合else条件的文件返回得很好,但if条件中的特定文件会导致问题。 Even if I don't modify the stream at all, it still hangs.即使我根本不修改流,它仍然挂起。

Yes, the default stream provided is read only because the data is only buffered for a short moment and flushed to the client.是的,提供的默认流是只读的,因为数据只缓冲了一小段时间并刷新到客户端。 Hence you can't rewind or read it.因此,您无法倒带或阅读它。

Your second attempt doesn't work because the original stream is never processed.您的第二次尝试不起作用,因为从未处理过原始流。 You replaced the response body stream entirely with MemoryStream and thrown away the original request, so there is never something written to it and the client waits forever.您完全用MemoryStream替换了响应主体流并丢弃了原始请求,因此永远不会写入任何内容并且客户端永远等待。

You must not forget, that the stream to the client is within the original stream, you can't just replace it with something else.您一定不要忘记,客户端的流在原始流中,您不能只是用其他东西替换它。

After calling await next() you must read the data from the MemoryStream and then write it to the original stream.调用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"
});

Remark评论

But be aware, that this solution will buffer the whole response into the servers memory, so if you send large files this will significantly degenerate the performance of your ASP.NET Core application, especially if you serve files which are several megabytes in size and cause the garbage collection to be triggered more often.但请注意,此解决方案会将整个响应缓冲到服务器内存中,因此如果您发送大文件,这将显着降低 ASP.NET Core 应用程序的性能,特别是如果您提供几兆字节大小的文件并导致更频繁地触发垃圾收集。

And this wouldn't only affect your static files, but also all your regular requests, because the MVC Middleware is called after the static files middleware.这不仅会影响您的静态文件,还会影响您的所有常规请求,因为 MVC 中间件是在静态文件中间件之后调用的。

If you really want to modify a single (or a list of files) on each request, I'd rather suggest you doing this inside a controller and route certain files there.如果您真的想在每个请求上修改单个(或文件列表),我宁愿建议您在控制器内执行此操作并将某些文件路由到那里。 Remember, if the given file is not found by the static files middleware, it will call the next one in chain until it comes to the mvc middleware.请记住,如果静态文件中间件没有找到给定的文件,它将调用链中的下一个文件,直到它到达 mvc 中间件。

Just setup a route there that will match a specific file or folder and route it to a controller.只需在那里设置一个匹配特定文件或文件夹的路由并将其路由到控制器。 Read the file in the controller and write it to the response stream or just return the new steam (using return File(stream, contentType);读取控制器中的文件并将其写入响应流或仅返回新的流(使用return File(stream, contentType);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM