asp.net-core - 如何在ASP.NET Core中间件中直接将响应主体设置为文件流?

标签 asp.net-core asp.net-core-webapi razor-pages asp.net-core-middleware

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

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


任何想法直接设置context.Response.Body的方法有什么问题吗?

注意:管道中的下一个中间件都将被跳过,无需进一步处理。

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

context.Response.Body = new MemoryStream(Encoding.UTF8.GetBytes(DateTime.Now.ToString()));

最佳答案

  • 不行。您永远不能直接这样做。
    请注意,context.Response.Body是对对象( HttpResponseStream )的引用,该对象是 initialized before,它可以在HttpContext 中使用。假定所有字节都写入此原始流中。如果您通过Bodycontext.Response.Body = a_new_Stream更改为引用(指向)新的流对象,则原始Stream完全不会更改。
    另外,如果您查看ASP.NET Core的源代码,您会发现团队总是在最后将复制包装流复制到原始流,而不是简单地替换(除非他们使用模拟对象对单元进行测试)溪流)。例如,SPA Prerendering middleware源代码:
        finally
        {
            context.Response.Body = originalResponseStream;
            ...
    
    以及 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);
        }
    
  • 作为一种解决方法,您可以将字节复制到原始流,如下所示:
    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);
        }
    }
    
    或者,如果您想使用自己的流包装器劫持原始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;
        }
    
  • 关于asp.net-core - 如何在ASP.NET Core中间件中直接将响应主体设置为文件流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58653564/

    相关文章:

    c# - 带有模型绑定(bind)的 Razor 页面 POST 多个表单

    c# - 为特定的 Razor Pages 路由调用特定的过滤器?

    asp.net-core - 具有独立数据库的 ASP.NET Core

    c# - 最小占用空间/准系统 ASP.NET Core WebAPI

    c# - QueryString.Add 不向 ASP.NET Core 中的请求查询字符串添加值

    asp.net-core-webapi - 使用 IOption 从 asp.net core 3.1 Web api 中的配置读取连接字符串

    asp.net-web-api - 带有 PUT 或 PATCH 动词的 ASP.NET 5 + MVC 6 + Web API Controller 返回 404 错误

    webpack - 将 webpack 与 dotnet run 捆绑在一起

    asp.net-core - 表单字段是必需的,即使未如此定义

    c# - Entity Framework Core 映射相关实体,仅当它不为空时