HttpContext.Response.Body.Position = 0 - 出现“指定的方法不受支持”错误

23

我设置了一些记录中间件,利用HttpContext获取和记录信息。

为了读取整个流,我需要将HttpResponse.Body的位置设置为0,但是无论我尝试什么,它都会抛出“指定的方法不受支持”并失败。

这对我来说非常奇怪,因为位置直接内置在HttpResponse.Body中,我以前也成功使用过它。

我还尝试使用HttpResponse.Body.Seek,结果相同。

现在我陷入了困境,任何帮助将不胜感激。

更新:当我将其移动到新的内存流中时,我能够使响应体的位置发生改变,然而,现在它返回一个空的响应体。

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);

                ...
            }
        }

响应流只能向前读取,一旦读取完毕就无法再次读取。请将该流替换为您可控制的流。这个中间件正在使用哪个平台? - Nkosi
@Badulake 每次有 HTTP 请求时都会调用此函数。 - expenguin
@Nksoi 这是在 .Net Core 中。我不确定你所说的“用你控制的流替换它”是什么意思。 - expenguin
我需要更多信息才能确定代码何时执行。是在Appstart方法,页面加载还是Ihttphandler?何时执行? - X.Otano
@Badulake 嗯,它在 appstart 方法中,并且每当有某种 http 请求时运行(例如,如果有人尝试访问我的读取或写入端点之一)。 - expenguin
显示剩余6条评论
4个回答

10
根据解决此GitHub问题的评论,您需要启用缓冲
为此,请将以下代码片段添加到您的Startup.cs Configure方法中:
app.Use(async (context, next) =>
{
    context.Request.EnableBuffering();
    await next();
});

8
我能够解决这个问题:
首先,我将响应设置为自己的内存流,并在流设置完成后调用 await _next(context):
using var responseBodyStream = new MemoryStream();
var previousBodyStream = context.Response.Body;
context.Response.Body = responseBodyStream;

await _next(context);

然后一旦我这样做了,我注意到返回的是一个空的正文,这是由于试图将一个空的正文设置为响应上下文所致。
responseBodyStream.Flush();
responseBodyStream.Seek(0, SeekOrigin.Begin);
await responseBodyStream.CopyToAsync(previousBodyStream);
context.Response.Body = previousBodyStream;

我删除了这行代码,然后一切开始正常运行。

使用 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; - tdav

1
你可能在响应体准备好之前访问了它。 将任务Invoke(HttpContext context)的执行推迟到执行管道中更晚的步骤(即准备就绪时)

1
我今天在我的Asp.Net Core API中遇到了这个问题。

enter image description here

问题在于,我忘记给我的API添加[FromBody]参数。在添加以下内容后,问题得到了解决。
[HttpPost("merkliste/create")]
public virtual async Task<IActionResult> MerklisteWorksheetCreate(string worksheetName, [FromBody] string elementDetailsArray)

enter image description here

希望它有所帮助。

3
在我的情况下,我的控制器操作中没有任何参数,因此可以使用[FromBody]修饰任何东西。 - Paul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接