29

I'm using an ASP.NET Core Azure Web App to provide a RESTful API to a client, and the client doesn't handle chunking correctly.

Is it possible to completely turn off Transfer-Encoding: chunked, either at the controller level or in file web.config?

I'm returning a JsonResult somewhat like this:

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

    return Json(AuthResultData);
}
6
  • 1
    Do you have the "Content-Length" header in the response? Commented Jun 22, 2016 at 14:11
  • I've added an example of what my action looks like. I don't add that header, as I have no idea how long the Json produced will be. Previously, ASP has added that header, but in recent servers it no longer does (possibly since the move to RC2) Commented Jun 22, 2016 at 14:26
  • 1
    You can do this with response buffering: github.com/aspnet/BasicMiddleware/blob/dev/samples/… Commented Jun 23, 2016 at 16:58
  • 1
    Interestingly that works on a local server, but not in Azure. I'm guessing a difference between Server: Kestrel and Server: Microsoft-IIS/8.0?? Commented Jun 23, 2016 at 18:15
  • 2
    Did you find a solution? I have the same problem which works on my local VS but doesn't work when I deploy it to Azure app services Commented Mar 2, 2017 at 18:27

4 Answers 4

17

How to get rid of chunking in .NET Core 2.2:

The trick is to read the response body into your own MemoryStream, so you can get the length. Once you do that, you can set the content-length header, and IIS won't chunk it. I assume this would work for Azure too, but I haven't tested it.

Here's the middleware:

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

Then add this in Startup:

app.UseMiddleware<DeChunkerMiddleware>();

It needs to be before app.UseMvC().

Sign up to request clarification or add additional context in comments.

5 Comments

Works well, though I assume there's inevitably some overhead in terms of memory and processing as you're in effect reading and copying the whole response. Just curious if you tried benchmarking this at all? I've implemented this but have added logic such that I only do this IF the incoming request had a "nochunk" header present, so clients can request an 'un-chunked' response, so I don't force all clients to live an unchunked existence.
Do not use OnStarting to set Content-Length, set it just before CopyToAsync.
If using app.Use(async (context, next) => {} style, make sure to place it before app.UseResponseCompression().
@Tratcher When I tried your suggestion the browser would hang forever. Using the original solution worked well.
4

In ASP.NET Core, this seems to work across hosts:

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

Indicates the identity function (i.e., no compression, nor modification). This token, except if explicitly specified, is always deemed acceptable.

This also works when you explicitly disable response buffering:

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

1 Comment

just a heads up that for me, with ASP.Net Core 2.2 neither of these solutions work. Adding those headers results in a sort of frozen response on my client end with an empty body.
3

It works in .NET Core 2.0. Just set ContentLength before writing the results into the response body stream.

In the startup class:

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

In xxxResultTranslatorStream:

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

1 Comment

This is great and all, but what is the Run method?
0

I found that all my chunking problems went away if I just returned a FileStream from Get() and let ASP.NET deal with the rest.

Microsoft software tends to work best if you just give up control and trust them. It tends to work worst if you actually try to control the process.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.