【问题标题】:Disable chunking in ASP.NET Core在 ASP.NET Core 中禁用分块
【发布时间】:2021-04-07 23:36:56
【问题描述】:

我正在使用 ASP.NET Core Azure Web App 向客户端提供 RESTful API,但客户端无法正确处理分块。

是否可以在控制器级别或文件 web.config 中完全关闭 Transfer-Encoding: chunked

我返回的 JsonResult 有点像这样:

[HttpPost]
[Produces("application/json")]
public IActionResult Post([FromBody] AuthRequest RequestData)
{
    AuthResult AuthResultData = new AuthResult();

    return Json(AuthResultData);
}

【问题讨论】:

  • 响应中有“Content-Length”标头吗?
  • 我添加了一个示例,说明我的操作是什么样的。我不添加该标题,因为我不知道 Json 生成的时间有多长。以前,ASP 已添加该标头,但在最近的服务器中不再添加(可能是因为迁移到 RC2)
  • 您可以通过响应缓冲来做到这一点:github.com/aspnet/BasicMiddleware/blob/dev/samples/…
  • 有趣的是,它可以在本地服务器上运行,但不能在 Azure 中运行。我猜Server: KestrelServer: Microsoft-IIS/8.0 之间的区别??
  • 您找到解决方案了吗?我有同样的问题,它适用于我的本地 VS,但当我将它部署到 Azure 应用程序服务时不起作用

标签: asp.net-core asp.net-core-mvc azure-web-app-service


【解决方案1】:

如何摆脱 .NET Core 2.2 中的分块:

诀窍是将响应正文读入你自己的MemoryStream,这样你就可以得到长度。完成此操作后,您可以设置 content-length 标头,IIS 不会对其进行分块。我认为这也适用于 Azure,但我还没有测试过。

这是中间件:

public class DeChunkerMiddleware
{
    private readonly RequestDelegate _next;

    public DeChunkerMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var originalBodyStream = context.Response.Body;
        using (var responseBody = new MemoryStream())
        {
            context.Response.Body = responseBody;
            long length = 0;
            context.Response.OnStarting(() =>
            {
                context.Response.Headers.ContentLength = length;
                return Task.CompletedTask;
            });
            await _next(context);

            // If you want to read the body, uncomment these lines.
            //context.Response.Body.Seek(0, SeekOrigin.Begin);
            //var body = await new StreamReader(context.Response.Body).ReadToEndAsync();

            length = context.Response.Body.Length;
            context.Response.Body.Seek(0, SeekOrigin.Begin);
            await responseBody.CopyToAsync(originalBodyStream);
        }
    }
}

然后在 Startup 中添加这个:

app.UseMiddleware<DeChunkerMiddleware>();

必须在app.UseMvC()之前。

【讨论】:

  • 完美修复了 mono droid 1.2 github.com/mono/mono/issues/9511 & stackoverflow.com/questions/52402634/… 中的错误
  • 效果很好,尽管我认为在内存和处理方面不可避免地会有一些开销,因为您实际上是在读取和复制整个响应。只是好奇您是否尝试过对此进行基准测试?我已经实现了这一点,但添加了逻辑,这样我只在传入请求存在“nochunk”标头时才执行此操作,因此客户端可以请求“未分块”响应,因此我不会强制所有客户端生活未分块的存在。
  • 不要使用 OnStarting 设置 Content-Length,在 CopyToAsync 之前设置。
【解决方案2】:

在 ASP.NET Core 中,这似乎可以跨主机工作:

response.Headers["Content-Encoding"] = "identity";
response.Headers["Transfer-Encoding"] = "identity";

表示恒等函数(即不压缩,也不 修改)。除非明确指定,否则此标记始终是 认为可以接受。

当您明确禁用响应缓冲时,这也有效:

var bufferingFeature = httpContext.Features.Get<IHttpBufferingFeature>();
bufferingFeature?.DisableResponseBuffering();

【讨论】:

  • 对我来说只是提醒一下,对于 ASP.Net Core 2.2,这些解决方案都不起作用。添加这些标头会导致我的客户端出现一种冻结响应,并且正文为空。
【解决方案3】:

它适用于 .NET Core 2.0。只需在将结果写入响应正文流之前设置ContentLength

在启动类中:

app.Use(async (ctx, next) =>
{
    var stream = new xxxResultTranslatorStream(ctx.Response.Body);
    ctx.Response.Body = stream;

    await Run(ctx, next);

    stream.Translate(ctx);
    ctx.Response.Body = stream.Stream;
});

在 xxxResultTranslatorStream 中:

ctx.Response.Headers.ContentLength = 40;
stream.Write(writeTargetByte, 0, writeTargetByte.Length);

【讨论】:

  • 这很好,但是 Run 方法是什么?
【解决方案4】:

我发现如果我只是从 Get() 返回一个 FileStream 并让 ASP.NET 处理其余部分,那么我所有的分块问题都会消失。

如果您放弃控制权并信任它们,Microsoft 软件往往会运行得最好。如果你真的试图控制这个过程,它往往效果最差。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-28
    • 1970-01-01
    • 2017-08-08
    • 1970-01-01
    • 2021-06-04
    • 2021-01-28
    • 2021-09-21
    相关资源
    最近更新 更多