Using ASP.NET Core's middleware to modify response body
Hey, where's my content?
Note: the issue referenced in this post has been fixed and will get released with ASP.NET Core 1.1.0.
While creating a small piece of middleware to modify the response body's content, I had trouble getting anything to appear in my browser. I would get a 200 response-code, but no content (i.e. Content-Length: 0). Here is an example that creates a similar setup:
public class Startup
{
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        app.Use(async (context, next) =>
        {
            var newContent = string.Empty;
            using (var newBody = new MemoryStream())
            {
                // We set the response body to our stream so we can read after the chain of middlewares have been called.
                context.Response.Body = newBody;
                await next();
                // Reset the body so nothing from the latter middlewares goes to the output.
                context.Response.Body = new MemoryStream();
                newBody.Seek(0, SeekOrigin.Begin);
                // newContent will be `Hello`.
                newContent = new StreamReader(newBody).ReadToEnd();
                newContent += ", World!";
                // Send our modified content to the response body.
                await context.Response.WriteAsync(newContent);
            }
        });
        app.Map("", configuration =>
        {
            configuration.Use(async (context, next) =>
            {
                await context.Response.WriteAsync("Hello");
            });
        });
    }
}
curl -i http://localhost:54122/
HTTP/1.1 200 OK
Server: Kestrel
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcd2lsbGlhbS1ib2dhLWl2XERvY3VtZW50c1xwcm9qZWN0c1wxMGstYXBhcnRcc3JjXDEwa0FwYXJ0?=
X-Powered-By: ASP.NET
Date: Thu, 25 Aug 2016 22:42:51 GMT
Content-Length: 0
Turns out this is a known issue and has an easy workaround which involves storing the original response stream and setting back to it after the middlewares are called:
public class Startup
{
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        app.Use(async (context, next) =>
        {
            var newContent = string.Empty;
            // Store the "pre-modified" response stream.
            var existingBody = context.Response.Body;
            using (var newBody = new MemoryStream())
            {
                // Previous code removed for brevity.
                await next();
                // Set the stream back to the original.
                context.Response.Body = existingBody;
                newBody.Seek(0, SeekOrigin.Begin);
                // Previous code removed for brevity.
            }
        });
        // Previous code removed for brevity.
    }
}
curl -i http://localhost:54122/
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Server: Kestrel
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcd2lsbGlhbS1ib2dhLWl2XERvY3VtZW50c1xwcm9qZWN0c1wxMGstYXBhcnRcc3JjXDEwa0FwYXJ0?=
X-Powered-By: ASP.NET
Date: Thu, 25 Aug 2016 22:49:59 GMT
Hello, World!