นี่เป็นกระทู้เก่าเล็กน้อย แต่ตั้งแต่ฉันมาถึงที่นี่ฉันคิดว่าจะโพสต์สิ่งที่ค้นพบเพื่อที่พวกเขาจะได้ช่วยเหลือผู้อื่น
ก่อนอื่นฉันมีปัญหาเดียวกันซึ่งฉันต้องการรับคำขอร่างกายและทำบางอย่างกับสิ่งนั้น (การบันทึก / การตรวจสอบ) แต่อย่างอื่นฉันต้องการให้จุดสิ้นสุดมีลักษณะเหมือนกัน
ดังนั้นดูเหมือนว่าการเรียก EnableBuffering () อาจทำเคล็ดลับได้ จากนั้นคุณสามารถทำ Seek (0, xxx) บนเนื้อหาและอ่านเนื้อหาอีกครั้ง ฯลฯ
อย่างไรก็ตามสิ่งนี้นำไปสู่ปัญหาถัดไปของฉัน ฉันได้รับข้อยกเว้น "การดำเนินการซิงโครนัสไม่ได้รับอนุญาต" เมื่อเข้าถึงปลายทาง ดังนั้นวิธีแก้ปัญหาคือการตั้งค่าคุณสมบัติ AllowSynchronousIO = true ในตัวเลือก มีหลายวิธีในการทำให้สำเร็จ (แต่ไม่สำคัญสำหรับรายละเอียดที่นี่ .. )
จากนั้นปัญหาต่อไปคือเมื่อฉันไปอ่านคำขอร่างกายได้รับการกำจัดไปแล้ว ฮึ. แล้วอะไรให้?
ฉันใช้ Newtonsoft.JSON เป็นตัวแยกวิเคราะห์ [FromBody] ของฉันในการเรียก endpiont นั่นคือสิ่งที่รับผิดชอบในการอ่านแบบซิงโครนัสและจะปิดสตรีมเมื่อดำเนินการเสร็จสิ้น วิธีการแก้? อ่านสตรีมก่อนที่จะไปถึงการแยกวิเคราะห์ JSON หรือไม่ แน่นอนว่าได้ผลและฉันลงเอยด้วยสิ่งนี้:
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);
}
}
}
}
ตอนนี้ฉันสามารถเข้าถึงเนื้อหาโดยใช้ HttpContextItems ["request_body"] ในจุดสิ้นสุดที่มีแอตทริบิวต์ [ReadRequestBodyIntoItems]
แต่ผู้ชายนี่ดูเหมือนห่วงมากเกินไปที่จะกระโดดผ่าน นี่คือจุดที่ฉันจบลงและฉันมีความสุขมากกับมัน
จุดสิ้นสุดของฉันเริ่มต้นจากสิ่งที่ต้องการ:
[HttpPost("")]
[ReadRequestBodyIntoItems]
[Consumes("application/json")]
public async Task<IActionResult> ReceiveSomeData([FromBody] MyJsonObjectType value)
{
val bodyString = HttpContext.Items["request_body"];
}
แต่มันง่ายกว่ามากที่จะเปลี่ยนลายเซ็นเช่นนี้:
[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);
}
}
ฉันชอบสิ่งนี้มากเพราะมันอ่าน body stream เพียงครั้งเดียวและฉันควบคุม deserialization ได้แล้ว แน่นอนว่ามันเป็นเรื่องดีถ้า ASP.NET core ทำเวทมนตร์นี้ให้ฉัน แต่ที่นี่ฉันไม่ต้องเสียเวลาอ่านสตรีมสองครั้ง (อาจจะบัฟเฟอร์ทุกครั้ง) และโค้ดก็ค่อนข้างชัดเจนและสะอาด
หากคุณต้องการฟังก์ชันนี้ในอุปกรณ์ปลายทางจำนวนมากบางทีวิธีการของมิดเดิลแวร์อาจจะสะอาดกว่าหรืออย่างน้อยคุณก็สามารถห่อหุ้มการแยกเนื้อหาลงในฟังก์ชันส่วนขยายเพื่อให้โค้ดมีความกระชับมากขึ้น
อย่างไรก็ตามฉันไม่พบแหล่งที่มาที่เกี่ยวข้องกับทั้ง 3 ด้านของปัญหานี้ดังนั้นโพสต์นี้ หวังว่านี่จะช่วยใครสักคน!
BTW: ใช้ ASP .NET Core 3.1