Đây là một chủ đề hơi cũ, nhưng kể từ khi tôi đến đây, tôi nghĩ rằng tôi sẽ đăng những phát hiện của mình để chúng có thể giúp những người khác.
Đầu tiên, tôi gặp vấn đề tương tự, nơi tôi muốn nhận Request.Body và làm gì đó với điều đó (ghi nhật ký / kiểm tra). Nhưng nếu không, tôi muốn điểm cuối trông giống nhau.
Vì vậy, có vẻ như lệnh gọi EnableBuffering () có thể thực hiện thủ thuật. Sau đó, bạn có thể thực hiện Tìm kiếm (0, xxx) trên phần thân và đọc lại nội dung, v.v.
Tuy nhiên, điều này dẫn đến vấn đề tiếp theo của tôi. Tôi sẽ nhận được ngoại lệ "Hoạt động đồng bộ không được phép" khi truy cập điểm cuối. Vì vậy, cách giải quyết là đặt thuộc tính AllowSynchronousIO = true, trong các tùy chọn. Có một số cách để thực hiện điều này (nhưng không quan trọng phải chi tiết ở đây ..)
VẬY, vấn đề tiếp theo là khi tôi đọc Request.Body thì nó đã được xử lý. Ặc. Vì vậy, những gì cho?
Tôi đang sử dụng Newtonsoft.JSON làm trình phân tích cú pháp [FromBody] của mình trong lệnh gọi endpiont. Đó là những gì chịu trách nhiệm cho việc đọc đồng bộ và nó cũng đóng luồng khi nó hoàn tất. Giải pháp? Đọc luồng trước khi đến với phân tích cú pháp JSON? Chắc chắn, điều đó hoạt động và tôi đã kết thúc với điều này:
/// <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);
}
}
}
}
Vì vậy, bây giờ, tôi có thể truy cập phần thân bằng cách sử dụng HttpContext.Items ["request_body"] trong các điểm cuối có thuộc tính [ReadRequestBodyIntoItems].
Nhưng anh bạn, điều này có vẻ như có quá nhiều vòng để nhảy qua. Vì vậy, đây là nơi tôi đã kết thúc và tôi thực sự hài lòng với nó.
Điểm cuối của tôi bắt đầu như sau:
[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...
}
Nhưng đơn giản hơn nhiều là chỉ cần thay đổi chữ ký, như sau:
[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...
}
}
Tôi thực sự thích điều này vì nó chỉ đọc luồng cơ thể một lần và tôi có quyền kiểm soát quá trình khử trên không. Chắc chắn, thật tuyệt nếu lõi ASP.NET thực hiện điều kỳ diệu này đối với tôi, nhưng ở đây tôi không lãng phí thời gian đọc luồng hai lần (có lẽ mỗi lần lưu vào bộ đệm) và mã khá rõ ràng và sạch sẽ.
Nếu bạn cần chức năng này trên nhiều thiết bị đầu cuối, có lẽ các phương pháp tiếp cận phần mềm trung gian có thể sạch hơn hoặc ít nhất bạn có thể đóng gói phần trích xuất phần thân vào một chức năng mở rộng để làm cho mã ngắn gọn hơn.
Dù sao, tôi không tìm thấy bất kỳ nguồn nào đề cập đến cả 3 khía cạnh của vấn đề này, do đó, bài đăng này. Hy vọng rằng điều này sẽ giúp ai đó!
BTW: Điều này đang sử dụng ASP .NET Core 3.1.