0

I am trying to write a middleware that calls the next middleware and then whatever the response body of that middleware is, it will be changed by my middleware.

This is what the Configuration() method in the Startup class looks like:

public void Configuration(IAppBuilder app)
{
    app.Use<OAuthAuthenticationFailCustomResponse>();
    app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}


This is the middleware written by me:

public class OAuthAuthenticationFailCustomResponse : OwinMiddleware
{
    public OAuthAuthenticationFailCustomResponse(OwinMiddleware next)
        : base(next)
    {
    }

    public async override Task Invoke(IOwinContext context)
    {
        var stream = context.Response.Body;

        using (var buffer = new MemoryStream())
        {
            context.Response.Body = buffer;

            await Next.Invoke(context);

            buffer.Seek(0, SeekOrigin.Begin);

            var byteArray = Encoding.ASCII.GetBytes("Hello World");
            context.Response.StatusCode = 200;
            context.Response.ContentLength = byteArray.Length;

            buffer.SetLength(0);
            buffer.Write(byteArray, 0, byteArray.Length);
            buffer.Seek(0, SeekOrigin.Begin);
            buffer.CopyTo(stream);
        }
    }
}


However, I still receive the response of the OAuthBearerAuthentication middleware after calling the API, which is:

{ "Message": "Authorization has been denied for this request." }


This is where I learnt to write the code that changes the response body;

Nkosi
  • 235,767
  • 35
  • 427
  • 472
Stoatman
  • 758
  • 3
  • 9
  • 22

1 Answers1

0

The original stream was not put back into the context response body

public async override Task Invoke(IOwinContext context) {
    // hold a reference to what will be the outbound/processed response stream object 
    var stream = context.Response.Body;

    // create a stream that will be sent to the response stream before processing
    using (var buffer = new MemoryStream()) {
         // set the response stream to the buffer to hold the unaltered response
        context.Response.Body = buffer;

        // allow other middleware to respond
        await Next.Invoke(context);

        // we have the unaltered response, go to start
        buffer.Seek(0, SeekOrigin.Begin);

        // read the stream from the buffer
        var reader = new StreamReader(buffer);
        string responseBody = reader.ReadToEnd();

        //...decide what you want to do with the responseBody from other middleware

        //custom response
        var byteArray = Encoding.ASCII.GetBytes("Hello World");

        //write custom response to stream
        stream.Write(byteArray, 0, byteArray.Length);

        //reset the response body back to the original stream
        context.Response.Body = stream;
        context.Response.StatusCode = 200;
        context.Response.ContentLength = byteArray.Length;
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • This is what I got: {"Message":"Authorization has been denied for this request."}Hello World. It seems like the string is just appended to the original response body. – Stoatman Jan 01 '19 at 10:55
  • Are you trying to replace the response body or alter it? – Nkosi Jan 01 '19 at 10:56
  • I am trying to replace the response body. The expected response body is "Hello World". – Stoatman Jan 01 '19 at 10:57
  • The following line threw System.NotSupportedException with message "Specified method is not supported.": stream.Seek(0, SeekOrigin.Begin); – Stoatman Jan 01 '19 at 11:07
  • @KhanhCTG then just remove it. – Nkosi Jan 01 '19 at 11:08
  • The expression stream.Length threw the same exception like above. I have replaced it with byteArray.Length, however it still does not work. The response body is still: {"Message":"Authorization has been denied for this request."}Hello World. – Stoatman Jan 01 '19 at 11:16