Dies ist ein bisschen wie ein alter Thread, aber seit ich hier bin, dachte ich, ich würde meine Ergebnisse veröffentlichen, damit sie anderen helfen können.
Erstens hatte ich das gleiche Problem, bei dem ich den Request.Body abrufen und etwas damit anfangen wollte (Protokollierung / Überwachung). Ansonsten wollte ich, dass der Endpunkt gleich aussieht.
Es schien also, als würde der Aufruf von EnableBuffering () den Trick machen. Dann können Sie eine Suche (0, xxx) am Körper durchführen und den Inhalt usw. erneut lesen.
Dies führte jedoch zu meiner nächsten Ausgabe. Beim Zugriff auf den Endpunkt werden Ausnahmen "Synchornous-Operationen sind nicht zulässig" angezeigt. Die Problemumgehung besteht darin, die Eigenschaft AllowSynchronousIO = true in den Optionen festzulegen. Es gibt eine Reihe von Möglichkeiten, um dies zu erreichen (aber nicht wichtig, um hier näher darauf einzugehen ..)
DANN ist das nächste Problem, dass, wenn ich die Anfrage lese, sie bereits entsorgt wurde. Pfui. Also, was gibt es?
Ich verwende Newtonsoft.JSON als meinen [FromBody] -Parser im Endpiont-Aufruf. Das ist es, was für die synchronen Lesevorgänge verantwortlich ist und es schließt auch den Stream, wenn es fertig ist. Lösung? Lesen Sie den Stream, bevor er zum JSON-Parsing gelangt? Klar, das funktioniert und ich bin dazu gekommen:
/// <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);
}
}
}
}
Jetzt kann ich mit HttpContext.Items ["request_body"] in den Endpunkten mit dem Attribut [ReadRequestBodyIntoItems] auf den Body zugreifen.
Aber Mann, das scheint viel zu viele Reifen zu sein, um durch zu springen. Hier habe ich also geendet und bin sehr zufrieden damit.
Mein Endpunkt begann wie folgt:
[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...
}
Es ist jedoch viel einfacher, die Signatur wie folgt zu ändern:
[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...
}
}
Das hat mir sehr gut gefallen, weil es den Körperstrom nur einmal liest und ich die Kontrolle über die Deserialisierung habe. Sicher, es ist schön, wenn der ASP.NET-Kern diese Magie für mich ausführt, aber hier verschwende ich keine Zeit damit, den Stream zweimal zu lesen (möglicherweise jedes Mal zu puffern), und der Code ist ziemlich klar und sauber.
Wenn Sie diese Funktionalität auf vielen Endpunkten benötigen, sind die Middleware-Ansätze möglicherweise sauberer, oder Sie können die Körperextraktion zumindest in eine Erweiterungsfunktion einkapseln, um den Code präziser zu gestalten.
Wie auch immer, ich habe keine Quelle gefunden, die alle drei Aspekte dieser Ausgabe berührt hat, daher dieser Beitrag. Hoffentlich hilft das jemandem!
Übrigens: Dies war mit ASP .NET Core 3.1.