Ini adalah utas lama, tetapi sejak saya tiba di sini, saya pikir saya akan memposting temuan saya sehingga mereka dapat membantu orang lain.
Pertama, saya memiliki masalah yang sama, di mana saya ingin mendapatkan Request.Body dan melakukan sesuatu dengan itu (logging / audit). Tetapi sebaliknya saya ingin titik akhir terlihat sama.
Jadi, sepertinya panggilan EnableBuffering () mungkin berhasil. Kemudian Anda dapat melakukan Seek (0, xxx) pada tubuh dan membaca kembali isinya, dll.
Namun, hal ini menyebabkan masalah saya berikutnya. Saya akan mendapatkan pengecualian "Operasi sinkronis tidak diizinkan" saat mengakses titik akhir. Jadi, solusinya adalah mengatur properti AllowSynchronousIO = true, dalam opsi. Ada beberapa cara untuk melakukannya (tetapi tidak penting untuk detailnya di sini ..)
LALU, masalah berikutnya adalah ketika saya pergi untuk membaca Request. Tubuh itu sudah dibuang. Ugh. Jadi, apa yang menyebabkannya?
Saya menggunakan Newtonsoft.JSON sebagai parser [FromBody] saya di panggilan endpiont. Itulah yang bertanggung jawab atas pembacaan sinkron dan juga menutup aliran saat selesai. Larutan? Baca aliran sebelum masuk ke penguraian JSON? Tentu, itu berhasil dan saya berakhir dengan ini:
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);
}
}
}
}
Jadi sekarang, saya dapat mengakses tubuh menggunakan HttpContext.Items ["request_body"] di titik akhir yang memiliki atribut [ReadRequestBodyIntoItems].
Tapi kawan, ini sepertinya terlalu banyak rintangan untuk dilewati. Jadi di sinilah saya mengakhiri, dan saya sangat senang dengannya.
Titik akhir saya dimulai sebagai sesuatu seperti:
[HttpPost("")]
[ReadRequestBodyIntoItems]
[Consumes("application/json")]
public async Task<IActionResult> ReceiveSomeData([FromBody] MyJsonObjectType value)
{
val bodyString = HttpContext.Items["request_body"];
}
Tetapi jauh lebih mudah untuk hanya mengubah tanda tangan, seperti:
[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);
}
}
Saya sangat menyukai ini karena hanya membaca aliran tubuh sekali, dan saya memiliki kendali atas deserialisasi. Tentu, bagus jika inti ASP.NET melakukan keajaiban ini untuk saya, tetapi di sini saya tidak membuang waktu membaca aliran dua kali (mungkin buffering setiap kali), dan kodenya cukup jelas dan bersih.
Jika Anda memerlukan fungsionalitas ini pada banyak titik akhir, mungkin pendekatan middleware mungkin lebih bersih, atau setidaknya Anda dapat merangkum ekstraksi tubuh ke dalam fungsi ekstensi untuk membuat kode lebih ringkas.
Bagaimanapun, saya tidak menemukan sumber apa pun yang menyentuh ketiga aspek masalah ini, karenanya posting ini. Semoga ini membantu seseorang!
BTW: Ini menggunakan ASP .NET Core 3.1.