如何在 ASP.NET Core 中间件中直接将响应正文设置为文件流?

Nic*_*ick 7 asp.net-core asp.net-core-middleware asp.net-core-webapi razor-pages

下面将文件流写入Response.BodyASP.NET Core 中间件的示例代码不起作用(发出空响应):

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

Run Code Online (Sandbox Code Playgroud)

任何想法直接设置context.Response.Body?

注意:管道中的任何下一个中间件都将被跳过,不再进行进一步处理。

更新(另一个例子):一个简单的MemoryStream分配也不起作用(空响应):

context.Response.Body = new MemoryStream(Encoding.UTF8.GetBytes(DateTime.Now.ToString()));
Run Code Online (Sandbox Code Playgroud)

itm*_*nus 11

  1. 不,你永远不能直接这样做。

    注意,context.Response.Body是为一个对象(基准HttpResponseStream),其之前初始化它成为可用HttpContext。假设所有字节都写入这个原始流。如果您将Bodyto 引用(指向)一个新的流对象更改为context.Response.Body = a_new_Stream,则原始流对象Stream根本不会更改。

    此外,如果您查看 的源代码ASP.NET Core,您会发现团队总是在最后将包装流复制到原始正文流,而不是简单的替换(除非他们使用模拟流进行单元测试)。例如,SPA Prerendering 中间件源代码:

        finally
        {
            context.Response.Body = originalResponseStream;
            ...
    
    Run Code Online (Sandbox Code Playgroud)

    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);
        }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 作为一种解决方法,您可以字节复制到原始流,如下所示:

    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);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    或者,如果您想使用自己的流包装器劫持原始数据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;
        }
    
    Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的解释。对于文件内容响应,我认为“httpContext.Response.SendFileAsync”也可以工作,完全不需要“FileStream”。 (5认同)