Modify middleware response

.NET Core 3+ solution with proper resource handling:

Replace response stream by MemoryStream to prevent its sending. Return the original stream after the response is modified:

public async Task Invoke(HttpContext context)
{
    var response = context.Response;

    //uncomment this line to re-read context.Request.Body stream
    //context.Request.EnableBuffering();
    
    var originBody = response.Body;
    using var newBody = new MemoryStream();
    response.Body = newBody;

    await _next(context);

    await ModifyResponseAsync(response);
    
    newBody.Seek(0, SeekOrigin.Begin);
    await newBody.CopyToAsync(originBody);
    response.Body = originBody;
}

private async Task ModifyResponseAsync(HttpResponse response)
{
    var stream = response.Body;
    using var reader = new StreamReader(stream, leaveOpen: true);
    string originalResponse = await reader.ReadToEndAsync();
    string modifiedResponse = "Hello from Stackoverflow";

    stream.SetLength(0);
    using var writer = new StreamWriter(stream, leaveOpen: true);
    await writer.WriteAsync(modifiedResponse);
    await writer.FlushAsync();
    response.ContentLength = stream.Length;
}

Original .NET Core 1 answer

Replace response stream by MemoryStream to prevent its sending. Return the original stream after the response is modified:

    public async Task Invoke(HttpContext context)
    {
        bool modifyResponse = true;
        Stream originBody = null;

        if (modifyResponse)
        {
            //uncomment this line only if you need to read context.Request.Body stream
            //context.Request.EnableRewind();

            originBody = ReplaceBody(context.Response);
        }

        await _next(context);

        if (modifyResponse)
        {
            //as we replaced the Response.Body with a MemoryStream instance before,
            //here we can read/write Response.Body
            //containing the data written by middlewares down the pipeline 

            //finally, write modified data to originBody and set it back as Response.Body value
            ReturnBody(context.Response, originBody);
        }
    }

    private Stream ReplaceBody(HttpResponse response)
    {
        var originBody = response.Body;
        response.Body = new MemoryStream();
        return originBody;
    }

    private void ReturnBody(HttpResponse response, Stream originBody)
    {
        response.Body.Seek(0, SeekOrigin.Begin);
        response.Body.CopyTo(originBody);
        response.Body = originBody;
    }

It’s a workaround and it can cause performance problems. I hope to see a better solution here.

Leave a Comment