替换 ASP.NET Core 1.0 中间件中的响应流

Replacing response stream in ASP.NET Core 1.0 middleware

我想在我的 ASP.NET Core 1.0 项目中编写自定义中间件,它将把原来框架的 Http Response Stream 替换为我自己的,这样我就可以对其执行读/查找/写操作(首先2 在原始流上是不可能的)在进一步的代码中,即在操作或过滤器中。

我从以下代码开始:

public class ReplaceStreamMiddleware
{
    protected RequestDelegate NextMiddleware;

    public ReplaceStreamMiddleware(RequestDelegate next)
    {
        NextMiddleware = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {       
        using (var responseStream = new MemoryStream())
        {
            var fullResponse = httpContext.Response.Body;
            httpContext.Response.Body = responseStream;
            await NextMiddleware.Invoke(httpContext);
            responseStream.Seek(0, SeekOrigin.Begin);
            await responseStream.CopyToAsync(fullResponse);
        }   
    }
}

以下代码的问题是 有时 fullResponse 流在调用 时已经关闭 await responseStream.CopyToAsync(fullResponse); 所以它抛出异常 无法访问已关闭的流。

当我在浏览器中加载页面然后刷新时,在它完全加载之前

很容易观察到这种奇怪的行为

我想知道:

  1. 为什么会这样?
  2. 如何预防?
  3. 我的解决方案是个好主意还是有另一种方法来替换响应流?

异常并非来自您的 CopyToAsync。它来自您的代码调用者之一:

您没有恢复 HttpContext 中的原始响应流。因此,无论谁调用你的中间件,都会得到一个关闭的 MemoryStream.

这是一些工作代码:

app.Use(async (httpContext, next) =>
{
    using (var memoryResponse = new MemoryStream())
    {
        var originalResponse = httpContext.Response.Body;
        try
        {
            httpContext.Response.Body = memoryResponse;

            await next.Invoke();

            memoryResponse.Seek(0, SeekOrigin.Begin);
            await memoryResponse.CopyToAsync(originalResponse);
        }
        finally
        {
            // This is what you're missing
            httpContext.Response.Body = originalResponse;
        }
    }
});

app.Run(async (context) =>
{
    context.Response.ContentType = "text/other";
    await context.Response.WriteAsync("Hello World!");
});