Search code examples
c#.net-coremiddlewarehttpcontext

C# DotNet Core Middleware Wrap Response


I have a simple controller action which looks like:

    public Task<IEnumerable<Data>> GetData()
    {
        IEnumerable<Data> data = new List<Data>();
        return data;
    }

I want to be able to inspect the return value from within the middleware so the JSON would look something like

{
  "data": [
  ],
  "apiVersion": "1.2",
  "otherInfoHere": "here"
}

So my payload always is within data. I know I can do this at a controller level but I don't wan to have to do it on every single action. I would rather do it in middleware once for all.

Here is an example of my middleware:

public class NormalResponseWrapper
{
    private readonly RequestDelegate next;

    public NormalResponseWrapper(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {                
        var obj = context;
        // DO something to get return value from obj
        // Create payload and set data to return value

        await context.Response.WriteAsync(/*RETURN NEW PAYLOAD HERE*/);
    }

Any ideas?

Got the value now but it's to late to return it

        try
        {
            using (var memStream = new MemoryStream())
            {
                context.Response.Body = memStream;
                await next(context);
                memStream.Position = 0;
                object responseBody = new StreamReader(memStream).ReadToEnd();
                memStream.Position = 0;
                await memStream.CopyToAsync(originalBody);
                // By now it is to late, above line sets the value that is going to be returned
                await context.Response.WriteAsync(new BaseResponse() { data = responseBody }.toJson());
            }

        }
        finally
        {
            context.Response.Body = originalBody;
        }

Solution

  • Review the comments to get an understanding of what you can do to wrap the response.

    public async Task Invoke(HttpContext context) {
        //Hold on to original body for downstream calls
        Stream originalBody = context.Response.Body;
        try {
            string responseBody = null;
            using (var memStream = new MemoryStream()) {
                //Replace stream for upstream calls.
                context.Response.Body = memStream;
                //continue up the pipeline
                await next(context);
                //back from upstream call.
                //memory stream now hold the response data
                //reset position to read data stored in response stream
                memStream.Position = 0;
                responseBody = new StreamReader(memStream).ReadToEnd();
            }//dispose of previous memory stream.
            //lets convert responseBody to something we can use
            var data = JsonConvert.DeserializeObject(responseBody);
            //create your wrapper response and convert to JSON
            var json = new BaseResponse() { 
                data = data, 
                apiVersion = "1.2",
                otherInfoHere = "here"
            }.toJson();
            //convert json to a stream
            var buffer = Encoding.UTF8.GetBytes(json);
            using(var output = new MemoryStream(buffer)) {
                await output.CopyToAsync(originalBody);
            }//dispose of output stream
        } finally {
            //and finally, reset the stream for downstream calls
            context.Response.Body = originalBody;
        }
    }