Read request body twice

If you’re using application/x-www-form-urlencoded or multipart/form-data, you can safely call context.Request.ReadFormAsync() multiple times as it returns a cached instance on subsequent calls.

If you’re using a different content type, you’ll have to manually buffer the request and replace the request body by a rewindable stream like MemoryStream. Here’s how you could do using an inline middleware (you need to register it soon in your pipeline):

app.Use(next => async context =>
{
    // Keep the original stream in a separate
    // variable to restore it later if necessary.
    var stream = context.Request.Body;

    // Optimization: don't buffer the request if
    // there was no stream or if it is rewindable.
    if (stream == Stream.Null || stream.CanSeek)
    {
        await next(context);

        return;
    }

    try
    {
        using (var buffer = new MemoryStream())
        {
            // Copy the request stream to the memory stream.
            await stream.CopyToAsync(buffer);

            // Rewind the memory stream.
            buffer.Position = 0L;

            // Replace the request stream by the memory stream.
            context.Request.Body = buffer;

            // Invoke the rest of the pipeline.
            await next(context);
        }
    }

    finally
    {
        // Restore the original stream.
        context.Request.Body = stream;
    }
});

You can also use the BufferingHelper.EnableRewind() extension, which is part of the Microsoft.AspNet.Http package: it’s based on a similar approach but relies on a special stream that starts buffering data in memory and spools everything to a temp file on disk when the threshold is reached:

app.Use(next => context =>
{
    context.Request.EnableRewind();

    return next(context);
});

FYI: a buffering middleware will probably be added to vNext in the future.

Leave a Comment