request.body 为空,即使有效负载被发送



我有一个PUT端点,它接收有效载荷并使用[FromBody]将其隐藏到对象。

在本地主机上运行时(使用 IIS Express(,这工作正常。

但是当我通过生产服务器 (IIS( 运行它时,它会失败。验证错误为:

{
"": [
"A non-empty request body is required."
]
}

我可以使用我的客户端代码和使用 Postman 重新创建它。

考虑到有效负载可能格式不正确,我使用了一些中间件(发布在 Stack Overflow 的其他地方(在它到达端点之前检查正文(参见下面的代码(,但即使在那个阶段,request.body 也是空的。

在此阶段,我看不到已经阅读请求的任何其他位置(因为我很欣赏可以清除它(。

我已经阅读了无数的 Stack Overflow 帖子和其他网页,似乎没有其他东西可以接近这种情况(request.body 为空,即使您可以在客户端中看到有效负载已发送(。

任何帮助将不胜感激。我很乐意提供任何进一步的细节。

作为参考,这里是代码。

有效载荷

{"currentlySaved":false,"type":"album"}

终点

// PUT api/<controller>/toggleSaveState
[HttpPut("toggleSaveState/{id}")]
public async Task<IActionResult> Put(string id, [FromBody] ToggleSaveStateRequest requestDetails)
{
...
}

表示有效负载的对象:

public class ToggleSaveStateRequest
{
public bool CurrentlySaved { get; set; }
public string Type { get; set; }
}

用于检查请求的中间件。

注意:无论我是否包含此中间件,都会出现问题。我已将其包含在内以诊断问题。

public class RequestResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
public RequestResponseLoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
//First, get the incoming request
var request = await FormatRequest(context.Request);
Console.WriteLine($"REQUEST: {request}");
//Copy a pointer to the original response body stream
var originalBodyStream = context.Response.Body;
//Create a new memory stream...
using (var responseBody = new MemoryStream())
{
//...and use that for the temporary response body
context.Response.Body = responseBody;
//Continue down the Middleware pipeline, eventually returning to this class
await _next(context);
//Format the response from the server
var response = await FormatResponse(context.Response);
//TODO: Save log to chosen datastore
Console.WriteLine($"RESPONSE: {response}");
//Copy the contents of the new memory stream (which contains the response) to the original stream, which is then returned to the client.
await responseBody.CopyToAsync(originalBodyStream);
}
}
private async Task<string> FormatRequest(HttpRequest request)
{
var body = request.Body;
//This line allows us to set the reader for the request back at the beginning of its stream.
request.EnableRewind();
//We now need to read the request stream.  First, we create a new byte[] with the same length as the request stream...
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
//...Then we copy the entire request stream into the new buffer.
await request.Body.ReadAsync(buffer, 0, buffer.Length);
//We convert the byte[] into a string using UTF8 encoding...
var bodyAsText = Encoding.UTF8.GetString(buffer);
//..and finally, assign the read body back to the request body, which is allowed because of EnableRewind()
request.Body = body;
return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
}
private async Task<string> FormatResponse(HttpResponse response)
{
//We need to read the response stream from the beginning...
response.Body.Seek(0, SeekOrigin.Begin);
//...and copy it into a string
string text = await new StreamReader(response.Body).ReadToEndAsync();
//We need to reset the reader for the response so that the client can read it.
response.Body.Seek(0, SeekOrigin.Begin);
//Return the string for the response, including the status code (e.g. 200, 404, 401, etc.)
return $"{response.StatusCode}: {text}";
}
}

编辑:

不带[FromBody]的端点版本:

下面就是为了简化我的解释。

// PUT api/<controller>/toggleSaveState
[HttpPut("toggleSaveState/{id}")]
public async Task<IActionResult> Put(string id)
{
using (var reader = new StreamReader(Request.Body))
{
var body = reader.ReadToEnd();
Console.WriteLine("body", body);
}
...
return Ok();
}

给定上述终结点(没有中间件(,当它在本地运行时,body的值为:

{"currentlySaved":false,"type":"album"}

但是,当它远程运行时,它是空的。

你的代码问题在这里:

//Copy a pointer to the original response body stream
var originalBodyStream = context.Response.Body;

您正在分配对响应流的引用,然后在此处使用空内存流覆盖(该流(它:

//...and use that for the temporary response body
context.Response.Body = responseBody;

因此,变量originalBodyStream中的引用现在也指向该空流。其他词 - 此时context.Response.Body的内容已丢失。 如果要将请求正文流复制到其他流,请使用以下命令:

await context.Response.Body.CopyToAsync(originalBodyStream);

请记住,流是引用类型,因此:

var bodyVar = request.Body;

仅分配对流的引用(而不是值!( - 换句话说,对request.Body的任何修改都将反映在bodyVar上,因为它指向内存中的同一位置。

相关内容

最新更新