Modify a static file response in an ASP.NET kernel

I serve a bunch of static files in my application using app.UseStaticFiles() . I would like to add some additional markup in the response for a specific HTML file before sending it. My first attempt was to add middleware like this to middleware static files:

 app.Use(async (context, next) => { await next(); // Modify the response here }); 

However, this does not work, because I cannot read the read response stream - it uses a Kestrel FrameResponseStream under a hood that is not readable.

So, I decided that I could replace the body stream of the MemoryStream response, which I could write:

 app.Use(async (context, next) => { context.Response.Body = new MemoryStream(); await next(); // Modify the response here }); 

But it just makes the request never end - it goes through all the stages of the pipeline, but it doesn't even return any headers to the browser.

So, is there a way to change the response that StaticFileMiddleware creates?


Update

Since the HTML file in question is tiny (765 bytes), memory consumption is not a concern. However, any attempts to read / modify the answer still cause the same problem as before (nothing returns). More specifically, this is what is 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); } }); 

Files that fall into the else clause are returned just fine, but the specific file in if causes problems. Even if I don’t modify the stream at all, it still hangs.

0
source share
1 answer

Yes, the default flow provided is only available because the data is buffered only for a short time and cleared from the client. Therefore, you cannot rewind or read it.

The second attempt does not work because the original thread is never processed. You completely replaced the response body stream with a MemoryStream and threw away the original request, so nothing is written on it, and the client waits forever.

You should not forget that the stream for the client is in the original stream, you cannot just replace it with something else.

After calling await next() you should read the data from the MemoryStream and then write it to the original stream.

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

Note

But keep in mind that this solution will buffer the whole response to server memory, so if you send large files, it will significantly degrade the performance of your ASP.NET Core application, especially if you submit files of several megabytes in size and often cause garbage collection.

And this will not only affect your static files, but also all your regular requests, because the MVC middleware is called after the static file middleware.

If you really want to change one (or a list of files) for each request, I would prefer that you do this inside the controller and send certain files there. Remember that if a given file is not found by the static file middleware, it will call the next one in the chain until it reaches the mvc middleware.

Just configure the route there, which will correspond to a specific file or folder and direct it to the controller. Read the file in the controller and write it to the response stream or just return the new pair (using return File(stream, contentType);

+2
source

Source: https://habr.com/ru/post/1263635/


All Articles