Este es un hilo un poco antiguo, pero desde que llegué aquí, pensé que publicaría mis hallazgos para que puedan ayudar a otros.
Primero, tuve el mismo problema, donde quería obtener el Request.Body y hacer algo con eso (registro / auditoría). Pero, por lo demás, quería que el punto final tuviera el mismo aspecto.
Entonces, parecía que la llamada EnableBuffering () podría hacer el truco. Luego puede hacer una búsqueda (0, xxx) en el cuerpo y volver a leer el contenido, etc.
Sin embargo, esto me llevó a mi próximo número. Obtendría excepciones de "Las operaciones sincronizadas no están permitidas" al acceder al punto final. Entonces, la solución alternativa es establecer la propiedad AllowSynchronousIO = true, en las opciones. Hay varias formas de lograr esto (pero no es importante detallar aquí ...)
ENTONCES, el siguiente problema es que cuando voy a leer la Solicitud, el cuerpo ya se ha eliminado. Ugh. Entonces, ¿qué pasa?
Estoy usando Newtonsoft.JSON como mi analizador [FromBody] en la llamada endpiont. Eso es lo que es responsable de las lecturas sincrónicas y también cierra la secuencia cuando está lista. ¿Solución? ¿Leer la transmisión antes de que llegue al análisis JSON? Claro, eso funciona y terminé con esto:
/// <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);
}
}
}
}
Así que ahora puedo acceder al cuerpo usando HttpContext.Items ["request_body"] en los puntos finales que tienen el atributo [ReadRequestBodyIntoItems].
Pero hombre, esto parece demasiados obstáculos para saltar. Así que aquí es donde terminé, y estoy realmente feliz con eso.
Mi punto final comenzó como algo como:
[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...
}
Pero es mucho más sencillo simplemente cambiar la firma, así:
[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...
}
}
Realmente me gustó esto porque solo lee el flujo del cuerpo una vez y yo tengo el control de la deserialización. Claro, es bueno si el núcleo de ASP.NET hace esta magia por mí, pero aquí no pierdo el tiempo leyendo la transmisión dos veces (quizás almacenando en búfer cada vez), y el código es bastante claro y limpio.
Si necesita esta funcionalidad en muchos puntos finales, quizás los enfoques de middleware podrían ser más limpios, o al menos puede encapsular la extracción del cuerpo en una función de extensión para hacer que el código sea más conciso.
De todos modos, no encontré ninguna fuente que tocara los 3 aspectos de este problema, de ahí esta publicación. ¡Ojalá esto ayude a alguien!
Por cierto: esto estaba usando ASP .NET Core 3.1.