HttpContext.Response.Body.Position = 0 - "Specified method is not supported" error
Asked Answered
T

4

26

I've got some logging middleware I've setup that grabs and logs information utilizing HttpContext.

I need to set the position of the HttpResponse.Body to 0 in order to read the whole stream, however, no matter what I try, it throws "Specified method is not supported" and fails.

This is very weird to me because position is built right into HttpResponse.Body and I've used it before successfully.

I also tried to use HttpResponse.Body.Seek with the same result.

At this point I'm stuck, any help would be appreciated.

UPDATE: I was able to get the response.body position to change once I moved it into a new memory stream, however, now it returns an empty body back.

public async Task Invoke(HttpContext context)
        {
            //Retrieve request & response
            var request = context.Request;
            var response = context.Response;

            if (request.Path != "/")
            {
                var reqBody = request.Body;
                var resBody = response.Body;
                string path = request.Path;
                string method = request.Method;
                string queryString = HttpUtility.UrlDecode(request.QueryString.ToString());
                int statusCode = context.Response.StatusCode;

                var buffer = new byte[Convert.ToInt32(request.ContentLength)];
                await request.Body.ReadAsync(buffer, 0, buffer.Length);
                var reqBodyText = Encoding.UTF8.GetString(buffer);
                request.Body = reqBody;

                var responseBodyStream = new MemoryStream();
                context.Response.Body = responseBodyStream;

                await _next(context);

                responseBodyStream.Seek(0, SeekOrigin.Begin);
                var resBodyText = new StreamReader(responseBodyStream).ReadToEnd();
                responseBodyStream.Seek(0, SeekOrigin.Begin);
                await responseBodyStream.CopyToAsync(context.Response.Body);

                ...
            }
        }
Tallia answered 3/7, 2018 at 19:23 Comment(11)
when are you invoking the method?Firman
Response stream is forward only. Once read that is it. replace the stream with on you control. What platform is this middle ware being used?Sutherlan
@Badulake this is being invoked every time there is an HTTP request.Tallia
@Nksoi This is in .Net Core. I'm not sure what you mean by "replace the stream with on you control"Tallia
I need more information to know when exactly is executed. Appstart method, page load?ihttphandler?when??Firman
@Badulake ah yeah, it's within the appstart method and it's run whenever there is some kind of http request (like if someone tries to hit one of my read or write endpoints)Tallia
Try the same code in the application end methodFirman
which framework are u using?Firman
@Badulake .Net Core 2.1Tallia
post more code about where the code is being executed. The whole class /context removing methods that are not relevantFirman
This isn't an issue with timing. I was able to get the response body by opening a new memory stream where I manipulate the response. However, once the using statement for the new memory stream completes, it closes the response body and sends back an empty body. I've updated my code above to reflect my changes.Tallia
T
8

I was able to solve this:

Firstly, I set the response to its own memory stream and call await _next(context) after the stream was set:

using var responseBodyStream = new MemoryStream();
var previousBodyStream = context.Response.Body;
context.Response.Body = responseBodyStream;

await _next(context);

Then once I did this, I noticed I was getting an empty body back, this was due to trying to set an empty body back as the response context:

responseBodyStream.Flush();
responseBodyStream.Seek(0, SeekOrigin.Begin);
await responseBodyStream.CopyToAsync(previousBodyStream);
context.Response.Body = previousBodyStream;

I removed this line and everything started working correctly.

Tallia answered 6/7, 2018 at 12:36 Comment(1)
using var responseBodyStream = new MemoryStream(); var previousBodyStream = context.Response.Body; context.Response.Body = responseBodyStream; await next(); responseBodyStream.Flush(); responseBodyStream.Seek(0, SeekOrigin.Begin); using StreamReader reader = new StreamReader(context.Response.Body, Encoding.UTF8); string responseJson = await reader.ReadToEndAsync(); responseBodyStream.Seek(0, SeekOrigin.Begin); await responseBodyStream.CopyToAsync(previousBodyStream); context.Response.Body = previousBodyStream;Tripodic
H
16

As per the comment that solved this GitHub issue, you need to enable buffering.

To do so, add the following snippet to your Startup.cs Configure method:

app.Use(async (context, next) =>
{
    context.Request.EnableBuffering();
    await next();
});
Hydrolyse answered 16/12, 2021 at 11:16 Comment(0)
T
8

I was able to solve this:

Firstly, I set the response to its own memory stream and call await _next(context) after the stream was set:

using var responseBodyStream = new MemoryStream();
var previousBodyStream = context.Response.Body;
context.Response.Body = responseBodyStream;

await _next(context);

Then once I did this, I noticed I was getting an empty body back, this was due to trying to set an empty body back as the response context:

responseBodyStream.Flush();
responseBodyStream.Seek(0, SeekOrigin.Begin);
await responseBodyStream.CopyToAsync(previousBodyStream);
context.Response.Body = previousBodyStream;

I removed this line and everything started working correctly.

Tallia answered 6/7, 2018 at 12:36 Comment(1)
using var responseBodyStream = new MemoryStream(); var previousBodyStream = context.Response.Body; context.Response.Body = responseBodyStream; await next(); responseBodyStream.Flush(); responseBodyStream.Seek(0, SeekOrigin.Begin); using StreamReader reader = new StreamReader(context.Response.Body, Encoding.UTF8); string responseJson = await reader.ReadToEndAsync(); responseBodyStream.Seek(0, SeekOrigin.Begin); await responseBodyStream.CopyToAsync(previousBodyStream); context.Response.Body = previousBodyStream;Tripodic
F
1

You are probably accesing the response body before it is ready. Postpone the execution of the Task Invoke(HttpContext context) to a more later step( when it is ready) in the execution pipeline

Firman answered 3/7, 2018 at 20:45 Comment(0)
I
1

I was facing this issue in my Asp.Net core API today.

enter image description here

The issue was, I forgot to add the [FromBody] parameter to my API. After adding the same as below, the issue was resolved.

[HttpPost("merkliste/create")]
public virtual async Task<IActionResult> MerklisteWorksheetCreate(string worksheetName, [FromBody] string elementDetailsArray)

enter image description here

Hope it helps.

Imaginative answered 6/6, 2019 at 15:25 Comment(1)
In my case I don't have any parameters in my controller action so can decorate anything with [FromBody]Incumber

© 2022 - 2024 — McMap. All rights reserved.