这有点老了,但是自从我到达这里以来,我想我会发布我的发现,以便它们可以对其他人有所帮助。
首先,我遇到了同样的问题,我想要获取Request.Body并对此进行处理(记录/审核)。但是否则,我希望端点看起来相同。
因此,似乎EnableBuffering()调用可以解决问题。然后,您可以在正文上执行Seek(0,xxx)并重新读取内容,依此类推。
但是,这导致了我的下一个问题。访问端点时,会出现“不允许进行Synchornous操作”异常。因此,解决方法是在选项中设置属性AllowSynchronousIO = true。有多种方法可以完成此操作(但在此处不进行详细说明就不重要了。)
然后,下一个问题是,当我去阅读Request.Body时,它已经被处理掉了。啊。那么,有什么用呢?
我在endpiont调用中将Newtonsoft.JSON用作我的[FromBody]解析器。这就是负责同步读取的原因,并且它在完成时也会关闭流。解?在进行JSON解析之前阅读流吗?当然,那行得通,我最终得到了这一点:
/// <summary>
/// quick and dirty middleware that enables buffering the request body
/// </summary>
/// <remarks>
/// this allows us to re-read the request body's inputstream so that we can capture the original request as is
/// </remarks>
public class ReadRequestBodyIntoItemsAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context == null) return;
// NEW! enable sync IO beacuse the JSON reader apparently doesn't use async and it throws an exception otherwise
var syncIOFeature = context.HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
var req = context.HttpContext.Request;
req.EnableBuffering();
// read the body here as a workarond for the JSON parser disposing the stream
if (req.Body.CanSeek)
{
req.Body.Seek(0, SeekOrigin.Begin);
// if body (stream) can seek, we can read the body to a string for logging purposes
using (var reader = new StreamReader(
req.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
bufferSize: 8192,
leaveOpen: true))
{
var jsonString = reader.ReadToEnd();
// store into the HTTP context Items["request_body"]
context.HttpContext.Items.Add("request_body", jsonString);
}
// go back to beginning so json reader get's the whole thing
req.Body.Seek(0, SeekOrigin.Begin);
}
}
}
}
因此,现在,我可以在具有[ReadRequestBodyIntoItems]属性的端点中使用HttpContext.Items [“ request_body”]访问正文。
但是,伙计,这似乎太多了。所以这是我结束的地方,对此我感到非常满意。
我的端点开始像这样:
[HttpPost("")]
[ReadRequestBodyIntoItems]
[Consumes("application/json")]
public async Task<IActionResult> ReceiveSomeData([FromBody] MyJsonObjectType value)
{
val bodyString = HttpContext.Items["request_body"];
// use the body, process the stuff...
}
但是,只需更改签名就更直接了,就像这样:
[HttpPost("")]
[Consumes("application/json")]
public async Task<IActionResult> ReceiveSomeData()
{
using (var reader = new StreamReader(
Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false
))
{
var bodyString = await reader.ReadToEndAsync();
var value = JsonConvert.DeserializeObject<MyJsonObjectType>(bodyString);
// use the body, process the stuff...
}
}
我真的很喜欢这个,因为它只读取一次体流,而且我可以控制反序列化。当然,如果ASP.NET core为我做到这一点很好,但是在这里我不会浪费时间两次读取流(也许每次都缓冲),并且代码非常清晰。
如果您在许多端点上都需要此功能,则中间件方法可能更简洁,或者至少可以将主体提取封装到扩展功能中,以使代码更简洁。
无论如何,我没有找到任何涉及此问题的所有三个方面的消息来源,因此这篇文章。希望这可以帮助某人!
顺便说一句:这是使用ASP .NET Core 3.1。