C'est un peu un vieux fil de discussion, mais depuis que je suis arrivé ici, je me suis dit que je publierais mes résultats afin qu'ils puissent aider les autres.
Tout d'abord, j'ai eu le même problème, où je voulais obtenir le Request.Body et faire quelque chose avec ça (journalisation / audit). Mais sinon, je voulais que le point final ait le même aspect.
Donc, il semblait que l'appel EnableBuffering () pourrait faire l'affaire. Ensuite, vous pouvez faire une recherche (0, xxx) sur le corps et relire le contenu, etc.
Cependant, cela a conduit à mon prochain numéro. J'obtiendrais des exceptions «Les opérations synchrones sont interdites» lors de l'accès au point final. Donc, la solution de contournement consiste à définir la propriété AllowSynchronousIO = true, dans les options. Il existe un certain nombre de façons d'accomplir cela (mais il n'est pas important de détailler ici.)
ALORS, le problème suivant est que lorsque je vais lire la requête, le corps a déjà été éliminé. Pouah. Alors, qu'est-ce qui donne?
J'utilise Newtonsoft.JSON comme analyseur [FromBody] dans l'appel endpiont. C'est ce qui est responsable des lectures synchrones et cela ferme également le flux une fois terminé. Solution? Lire le flux avant de passer à l'analyse JSON? Bien sûr, cela fonctionne et je me suis retrouvé avec ceci:
/// <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);
}
}
}
}
Alors maintenant, je peux accéder au corps en utilisant le HttpContext.Items ["request_body"] dans les points de terminaison qui ont l'attribut [ReadRequestBodyIntoItems].
Mais mec, cela semble être beaucoup trop de cerceaux à franchir. Alors, voici où j'ai terminé, et j'en suis vraiment content.
Mon point de terminaison a commencé comme quelque chose comme:
[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...
}
Mais il est beaucoup plus simple de simplement changer la signature, comme ceci:
[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...
}
}
J'ai vraiment aimé cela car il ne lit qu'une seule fois le flux corporel et j'ai le contrôle de la désérialisation. Bien sûr, c'est bien si ASP.NET core fait cette magie pour moi, mais ici je ne perds pas de temps à lire le flux deux fois (peut-être en tamponnant à chaque fois), et le code est assez clair et propre.
Si vous avez besoin de cette fonctionnalité sur de nombreux points de terminaison, peut-être que les approches middleware pourraient être plus propres, ou vous pouvez au moins encapsuler l'extraction de corps dans une fonction d'extension pour rendre le code plus concis.
Quoi qu'il en soit, je n'ai trouvé aucune source qui aborde les 3 aspects de ce problème, d'où cet article. Espérons que cela aide quelqu'un!
BTW: cela utilisait ASP .NET Core 3.1.