To trochę stary wątek, ale odkąd tu dotarłem, pomyślałem, że opublikuję moje odkrycia, aby mogli pomóc innym.
Najpierw miałem ten sam problem, w którym chciałem pobrać Request.Body i coś z tym zrobić (logowanie / audyt). Ale poza tym chciałem, żeby punkt końcowy wyglądał tak samo.
Wydawało się więc, że wywołanie EnableBuffering () może załatwić sprawę. Następnie możesz wykonać wyszukiwanie (0, xxx) na ciele i ponownie przeczytać zawartość itp.
Doprowadziło to jednak do mojego następnego wydania. Otrzymywałbym wyjątki „Synchornous operacje są zabronione” podczas uzyskiwania dostępu do punktu końcowego. Tak więc obejście polega na ustawieniu właściwości AllowSynchronousIO = true w opcjach. Można to osiągnąć na wiele sposobów (ale nie ma znaczenia, aby tutaj szczegółowo opisywać).
WTEDY następną kwestią jest to, że kiedy idę przeczytać Wniosek, Ciało zostało już usunięte. Fuj. Więc co daje?
Używam Newtonsoft.JSON jako mojego parsera [FromBody] w wywołaniu endpiont. To właśnie jest odpowiedzialne za synchroniczne odczyty, a także zamyka strumień, gdy jest zakończony. Rozwiązanie? Odczytać strumień, zanim przejdzie do analizy JSON? Jasne, to działa i skończyło się na tym:
public class ReadRequestBodyIntoItemsAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context == null) return;
var syncIOFeature = context.HttpContext.Features.Get<IHttpBodyControlFeature>();
if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
var req = context.HttpContext.Request;
req.EnableBuffering();
if (req.Body.CanSeek)
{
req.Body.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(
req.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
bufferSize: 8192,
leaveOpen: true))
{
var jsonString = reader.ReadToEnd();
context.HttpContext.Items.Add("request_body", jsonString);
}
req.Body.Seek(0, SeekOrigin.Begin);
}
}
}
}
Więc teraz mogę uzyskać dostęp do treści przy użyciu HttpContext.Items ["request_body"] w punktach końcowych, które mają atrybut [ReadRequestBodyIntoItems].
Ale człowieku, wydaje się, że to zbyt wiele obręczy, aby przez nie przeskoczyć. Więc tutaj skończyłem i jestem z tego naprawdę zadowolony.
Mój punkt końcowy zaczął się jako coś takiego:
[HttpPost("")]
[ReadRequestBodyIntoItems]
[Consumes("application/json")]
public async Task<IActionResult> ReceiveSomeData([FromBody] MyJsonObjectType value)
{
val bodyString = HttpContext.Items["request_body"];
}
Ale o wiele łatwiej jest po prostu zmienić podpis, na przykład:
[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);
}
}
Bardzo mi się to podobało, ponieważ odczytuje strumień treści tylko raz i mam kontrolę nad deserializacją. Jasne, fajnie, że ASP.NET core robi za mnie tę magię, ale tutaj nie tracę czasu na dwukrotne czytanie strumienia (być może buforowanie za każdym razem), a kod jest dość przejrzysty i czysty.
Jeśli potrzebujesz tej funkcji w wielu punktach końcowych, być może podejście do oprogramowania pośredniczącego może być czystsze lub możesz przynajmniej hermetyzować wyodrębnianie treści do funkcji rozszerzającej, aby kod był bardziej zwięzły.
W każdym razie nie znalazłem żadnego źródła, które poruszyłoby wszystkie 3 aspekty tego zagadnienia, stąd ten post. Mam nadzieję, że to komuś pomoże!
BTW: używano ASP .NET Core 3.1.