จะอ่านเนื้อหาคำขอในตัวควบคุม webapi หลักของ asp.net ได้อย่างไร


121

ฉันพยายามอ่านเนื้อหาคำขอในOnActionExecutingวิธีการ แต่ฉันมักจะเข้าใจnullร่างกาย

var request = context.HttpContext.Request;
var stream = new StreamReader(request.Body);
var body = stream.ReadToEnd();

ฉันพยายามตั้งค่าตำแหน่งสตรีมอย่างชัดเจนเป็น 0 แต่ก็ไม่ได้ผลเช่นกัน เนื่องจากนี่คือ ASP.NET Core ฉันคิดว่าสิ่งต่าง ๆ แตกต่างกันเล็กน้อย ฉันสามารถดูตัวอย่างทั้งหมดได้ที่นี่ซึ่งอ้างอิงถึง Web API เวอร์ชันเก่า

มีวิธีอื่นในการทำเช่นนี้หรือไม่?


5
โปรดใช้ความระมัดระวังหากมีการอ่านเนื้อหาของคำขอก่อนหน้านี้ระหว่างไปป์ไลน์คำขอแสดงว่าว่างเปล่าเมื่อคุณพยายามอ่านครั้งที่สอง
Fabio

1
อาจซ้ำกันของRead HttpContent ในตัวควบคุม WebApi
Fabio

@Fabio ขอบคุณสำหรับข้อมูลเราสามารถกำหนดตำแหน่งและอ่านอีกครั้งได้หรือไม่?
กสิณโกศวรรธนะ

@KasunKoswattha - โดยการออกแบบเนื้อหาของร่างกายจะถือว่าเป็นสตรีมไปข้างหน้าเท่านั้นที่สามารถอ่านได้เพียงครั้งเดียว
user270576

ฉันเดาว่าคำถามค่อนข้างกำหนดเป้าหมายตัวกรองหรือมิดเดิลแวร์มากกว่าตัวควบคุม
Jim Aho

คำตอบ:


119

ใน ASP.Net Core ดูเหมือนจะซับซ้อนในการอ่านคำขอของร่างกายหลายครั้งอย่างไรก็ตามหากความพยายามครั้งแรกของคุณทำอย่างถูกวิธีคุณควรจะทำได้ในครั้งต่อไป

ฉันอ่านการตอบสนองหลายอย่างเช่นการเปลี่ยนบอดี้สตรีม แต่ฉันคิดว่าสิ่งต่อไปนี้สะอาดที่สุด:

จุดที่สำคัญที่สุดคือ

  1. เพื่อให้คำขอทราบว่าคุณจะอ่านเนื้อหาสองครั้งหรือมากกว่านั้น
  2. เพื่อไม่ปิดสตรีมของร่างกายและ
  3. เพื่อย้อนกลับไปยังตำแหน่งเริ่มต้นเพื่อไม่ให้กระบวนการภายในหายไป

[แก้ไข]

ตามที่ Murad ชี้ไว้คุณอาจใช้ประโยชน์จากส่วนขยาย. Net Core 2.1: EnableBufferingมันเก็บคำขอขนาดใหญ่ลงในดิสก์แทนที่จะเก็บไว้ในหน่วยความจำหลีกเลี่ยงปัญหาสตรีมขนาดใหญ่ที่เก็บไว้ในหน่วยความจำ (ไฟล์, รูปภาพ, ... ) . คุณสามารถเปลี่ยนโฟลเดอร์ชั่วคราวได้โดยตั้งค่าASPNETCORE_TEMPตัวแปรสภาพแวดล้อมและไฟล์จะถูกลบเมื่อคำขอสิ้นสุดลง

ใน AuthorizationFilterคุณสามารถทำสิ่งต่อไปนี้:

// Helper to enable request stream rewinds
using Microsoft.AspNetCore.Http.Internal;
[...]
public class EnableBodyRewind : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var bodyStr = "";
        var req = context.HttpContext.Request;

        // Allows using several time the stream in ASP.Net Core
        req.EnableRewind(); 

        // Arguments: Stream, Encoding, detect encoding, buffer size 
        // AND, the most important: keep stream opened
        using (StreamReader reader 
                  = new StreamReader(req.Body, Encoding.UTF8, true, 1024, true))
        {
            bodyStr = reader.ReadToEnd();
        }

        // Rewind, so the core is not lost when it looks the body for the request
        req.Body.Position = 0;

        // Do whatever work with bodyStr here

    }
}



public class SomeController : Controller
{
    [HttpPost("MyRoute")]
    [EnableBodyRewind]
    public IActionResult SomeAction([FromBody]MyPostModel model )
    {
        // play the body string again
    }
}

จากนั้นคุณสามารถใช้ร่างกายอีกครั้งในตัวจัดการคำขอ

ในกรณีของคุณถ้าคุณได้ผลลัพธ์ที่เป็นโมฆะอาจหมายความว่ามีการอ่านเนื้อหาในขั้นตอนก่อนหน้านี้แล้ว ในกรณีนี้คุณอาจต้องใช้มิดเดิลแวร์ (ดูด้านล่าง)

อย่างไรก็ตามโปรดใช้ความระมัดระวังหากคุณจัดการกับสตรีมขนาดใหญ่พฤติกรรมดังกล่าวแสดงว่าทุกอย่างถูกโหลดลงในหน่วยความจำสิ่งนี้ไม่ควรถูกเรียกใช้ในกรณีที่มีการอัปโหลดไฟล์

คุณอาจต้องการใช้สิ่งนี้เป็นมิดเดิลแวร์

ของฉันมีลักษณะเช่นนี้ (อีกครั้งหากคุณดาวน์โหลด / อัปโหลดไฟล์ขนาดใหญ่ควรปิดการใช้งานเพื่อหลีกเลี่ยงปัญหาหน่วยความจำ):

public sealed class BodyRewindMiddleware
{
    private readonly RequestDelegate _next;

    public BodyRewindMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try { context.Request.EnableRewind(); } catch { }
        await _next(context);
        // context.Request.Body.Dipose() might be added to release memory, not tested
    }
}
public static class BodyRewindExtensions
{
    public static IApplicationBuilder EnableRequestBodyRewind(this IApplicationBuilder app)
    {
        if (app == null)
        {
            throw new ArgumentNullException(nameof(app));
        }

        return app.UseMiddleware<BodyRewindMiddleware>();
    }

}

สตรีมยังคงว่างเปล่าแม้ว่าฉันจะย้อนกลับไปที่ตำแหน่ง 0
ก็ตาม

2
คุณเคยใช้ req.EnableRewind();หรือไม่? ฉันใช้รหัสด้านบนและทำงานได้ดี
Jean

1
ได้ใช้ req.EnableRewind (); ไม่ทำงาน, ไม่เป็นผล. ฉันได้ตำแหน่ง = 0 ความยาวลำตัว = 26 แต่การอ่านสตรีม 'ร่างกาย' จะมีสตริงว่างเปล่า
Adrian Nasui

นี่คือคำตอบที่สมบูรณ์แบบ
Gayan

3
นอกจากนี้ยังสามารถใช้request.EnableBuffering()(wrapper over EnableRewind()) มีอยู่ใน ASP.NET Core 2.1 docs.microsoft.com/en-us/dotnet/api/…
Murad

34

โซลูชันที่ชัดเจนกว่าทำงานใน ASP.Net Core 2.1 / 3.1

ชั้นกรอง

using Microsoft.AspNetCore.Authorization;
// For ASP.NET 2.1
using Microsoft.AspNetCore.Http.Internal;
// For ASP.NET 3.1
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;

public class ReadableBodyStreamAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        // For ASP.NET 2.1
        // context.HttpContext.Request.EnableRewind();
        // For ASP.NET 3.1
        // context.HttpContext.Request.EnableBuffering();
    }
}

ในคอนโทรลเลอร์

[HttpPost]
[ReadableBodyStream]
public string SomePostMethod()
{
    //Note: if you're late and body has already been read, you may need this next line
    //Note2: if "Note" is true and Body was read using StreamReader too, then it may be necessary to set "leaveOpen: true" for that stream.
    HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);

    using (StreamReader stream = new StreamReader(HttpContext.Request.Body))
    {
        string body = stream.ReadToEnd();
        // body = "param=somevalue&param2=someothervalue"
    }
}

5
สำหรับ netcore3.0 จะเป็น. EnableBuffering ()แทน.EnableRewind()
mr5

ขอบคุณ @ mr5 - อัปเดตคำตอบของฉัน
Andriod

ฉันพบสิ่งนี้ในขณะที่แก้ไขการอัปเกรด. net Core 2.2 -> Core 3.1 ที่ทำให้วิธี EnableRewind () เสียหาย ฉันคิดว่าสิ่งนี้ต้องการโค้ดอีกหนึ่งบรรทัดโดยที่ฉันไม่สามารถอ่าน Body: HttpContext.Request.Body.Seek (0, SeekOrigin Begin);
Larry Smith

2
สิ่งนี้ใช้ได้กับฉันเท่านั้นหลังจากเปลี่ยนAuthorizeAttributeเป็นAttribute(ใน ASP.Net Core 3.1)
Sigmund

โปรดอย่าลืมเพิ่มไลบรารีดังกล่าว ฉันมีรหัสแล้ว แต่ EnableBuffering แสดงเส้นสีแดง squiggly จนกระทั่งฉันรู้ว่าการอ้างอิง Microsoft.AspNetCore.Http หายไป ขอบคุณ Android!
kaarthick raman

19

เพื่อให้สามารถย้อนกลับเนื้อหาคำขอได้คำตอบของ @ Jean ช่วยให้ฉันสามารถหาวิธีแก้ปัญหาที่ดูเหมือนจะทำงานได้ดี ปัจจุบันฉันใช้สิ่งนี้สำหรับ Global Exception Handler Middleware แต่หลักการก็เหมือนกัน

ฉันสร้างมิดเดิลแวร์ที่เปิดใช้งานการกรอกลับในเนื้อหาคำขอโดยทั่วไป (แทนที่จะเป็นมัณฑนากร)

using Microsoft.AspNetCore.Http.Internal;
[...]
public class EnableRequestRewindMiddleware
{
    private readonly RequestDelegate _next;

    public EnableRequestRewindMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        context.Request.EnableRewind();
        await _next(context);
    }
}

public static class EnableRequestRewindExtension
{
    public static IApplicationBuilder UseEnableRequestRewind(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<EnableRequestRewindMiddleware>();
    }
}

จากนั้นสามารถใช้ในสิ่งที่คุณStartup.csชอบได้:

[...]
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    [...]
    app.UseEnableRequestRewind();
    [...]
}

ด้วยวิธีนี้ฉันสามารถย้อนกลับสตรีมเนื้อหาคำขอได้สำเร็จ


1
มันใช้งานได้ดีสำหรับฉัน @SaoBiz ขอบคุณ! หนึ่ง typo เปลี่ยนแปลงที่ 2 นี้จะสร้างUseEnableRequestRewind(this IApplicationBuilder builder)ใน
Richard Logwood

@RichardLogwood ดีใจที่ช่วยได้! ขอบคุณที่ค้นหาคำผิด! แก้ไขแล้ว. :)
SaoBiz

8

นี่เป็นกระทู้เก่าเล็กน้อย แต่ตั้งแต่ฉันมาถึงที่นี่ฉันคิดว่าจะโพสต์สิ่งที่ค้นพบเพื่อที่พวกเขาจะได้ช่วยเหลือผู้อื่น

ก่อนอื่นฉันมีปัญหาเดียวกันซึ่งฉันต้องการรับคำขอร่างกายและทำบางอย่างกับสิ่งนั้น (การบันทึก / การตรวจสอบ) แต่อย่างอื่นฉันต้องการให้จุดสิ้นสุดมีลักษณะเหมือนกัน

ดังนั้นดูเหมือนว่าการเรียก EnableBuffering () อาจทำเคล็ดลับได้ จากนั้นคุณสามารถทำ Seek (0, xxx) บนเนื้อหาและอ่านเนื้อหาอีกครั้ง ฯลฯ

อย่างไรก็ตามสิ่งนี้นำไปสู่ปัญหาถัดไปของฉัน ฉันได้รับข้อยกเว้น "การดำเนินการซิงโครนัสไม่ได้รับอนุญาต" เมื่อเข้าถึงปลายทาง ดังนั้นวิธีแก้ปัญหาคือการตั้งค่าคุณสมบัติ AllowSynchronousIO = true ในตัวเลือก มีหลายวิธีในการทำให้สำเร็จ (แต่ไม่สำคัญสำหรับรายละเอียดที่นี่ .. )

จากนั้นปัญหาต่อไปคือเมื่อฉันไปอ่านคำขอร่างกายได้รับการกำจัดไปแล้ว ฮึ. แล้วอะไรให้?

ฉันใช้ Newtonsoft.JSON เป็นตัวแยกวิเคราะห์ [FromBody] ของฉันในการเรียก endpiont นั่นคือสิ่งที่รับผิดชอบในการอ่านแบบซิงโครนัสและจะปิดสตรีมเมื่อดำเนินการเสร็จสิ้น วิธีการแก้? อ่านสตรีมก่อนที่จะไปถึงการแยกวิเคราะห์ JSON หรือไม่ แน่นอนว่าได้ผลและฉันลงเอยด้วยสิ่งนี้:

 /// <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);
            }
        }
    }
}

ตอนนี้ฉันสามารถเข้าถึงเนื้อหาโดยใช้ HttpContextItems ["request_body"] ในจุดสิ้นสุดที่มีแอตทริบิวต์ [ReadRequestBodyIntoItems]

แต่ผู้ชายนี่ดูเหมือนห่วงมากเกินไปที่จะกระโดดผ่าน นี่คือจุดที่ฉันจบลงและฉันมีความสุขมากกับมัน

จุดสิ้นสุดของฉันเริ่มต้นจากสิ่งที่ต้องการ:

[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...
}

แต่มันง่ายกว่ามากที่จะเปลี่ยนลายเซ็นเช่นนี้:

[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...
    }
}

ฉันชอบสิ่งนี้มากเพราะมันอ่าน body stream เพียงครั้งเดียวและฉันควบคุม deserialization ได้แล้ว แน่นอนว่ามันเป็นเรื่องดีถ้า ASP.NET core ทำเวทมนตร์นี้ให้ฉัน แต่ที่นี่ฉันไม่ต้องเสียเวลาอ่านสตรีมสองครั้ง (อาจจะบัฟเฟอร์ทุกครั้ง) และโค้ดก็ค่อนข้างชัดเจนและสะอาด

หากคุณต้องการฟังก์ชันนี้ในอุปกรณ์ปลายทางจำนวนมากบางทีวิธีการของมิดเดิลแวร์อาจจะสะอาดกว่าหรืออย่างน้อยคุณก็สามารถห่อหุ้มการแยกเนื้อหาลงในฟังก์ชันส่วนขยายเพื่อให้โค้ดมีความกระชับมากขึ้น

อย่างไรก็ตามฉันไม่พบแหล่งที่มาที่เกี่ยวข้องกับทั้ง 3 ด้านของปัญหานี้ดังนั้นโพสต์นี้ หวังว่านี่จะช่วยใครสักคน!

BTW: ใช้ ASP .NET Core 3.1


ถ้าโปรแกรมไม่สามารถแยกวิเคราะห์สตริง JSON เป็น NyObjectType แสดงว่าฉันอ่านค่าจาก "request_body" ไม่ได้
Ericyu67

6

วิธีที่รวดเร็วในการเพิ่มการตอบสนองบัฟเฟอร์ใน. NET Core 3.1 คือ

    app.Use((context, next) =>
    {
        context.Request.EnableBuffering();
        return next();
    });

ใน Startup.cs ฉันพบว่าสิ่งนี้ยังรับประกันได้ว่าจะเปิดใช้งานบัฟเฟอร์ก่อนที่จะอ่านสตรีมซึ่งเป็นปัญหาสำหรับ. Net Core 3.1 พร้อมคำตอบของตัวกรองตัวกลาง / การอนุญาตอื่น ๆ ที่ฉันเคยเห็น

จากนั้นคุณสามารถอ่านเนื้อหาคำขอของคุณผ่านHttpContext.Request.Bodyในตัวจัดการของคุณตามที่คนอื่น ๆ แนะนำ

สิ่งที่ควรพิจารณาก็คือEnableBufferingมีโอเวอร์โหลดที่ช่วยให้คุณสามารถ จำกัด จำนวนที่จะบัฟเฟอร์ในหน่วยความจำก่อนที่จะใช้ไฟล์ชั่วคราวและยัง จำกัด บัฟเฟอร์โดยรวมให้คุณด้วย หมายเหตุหากคำขอเกินขีด จำกัด นี้จะมีการโยนข้อยกเว้นและคำขอจะไม่ส่งถึงผู้จัดการของคุณ


สิ่งนี้ได้ผลดีสำหรับฉัน (3.1) อ้างถึงคุณในคำถามอื่น: stackoverflow.com/a/63525694/6210068
Lance

ทำงานบน 3.1. นอกจากนี้คำเตือนสำหรับผู้ใช้ที่จะใช้สิ่งนี้: อย่าลืมวางไว้ในลำดับที่ถูกต้องใน Startup.cs
Ebram Shehata

@EbramShehata - คำสั่งที่ถูกต้องคืออะไร?
Konstantin Konstantinov

3

ฉันมีปัญหาคล้ายกันเมื่อใช้ ASP.NET Core 2.1:

  • ฉันต้องการมิดเดิลแวร์ที่กำหนดเองเพื่ออ่านข้อมูล POSTed และทำการตรวจสอบความปลอดภัยบางอย่างกับมัน
  • การใช้ตัวกรองการอนุญาตนั้นใช้ไม่ได้จริงเนื่องจากมีการดำเนินการจำนวนมากที่ได้รับผลกระทบ
  • ฉันต้องอนุญาตให้มีการผูกวัตถุในการกระทำ ([FromBody] someObject) ขอบคุณที่SaoBizชี้ให้เห็นวิธีแก้ปัญหานี้

ดังนั้นวิธีแก้ปัญหาที่ชัดเจนคือการอนุญาตให้ย้อนกลับได้ แต่ตรวจสอบให้แน่ใจว่าหลังจากอ่านเนื้อหาแล้วการผูกยังคงใช้งานได้

EnableRequestRewindMiddleware

public class EnableRequestRewindMiddleware
{
    private readonly RequestDelegate _next;

    ///<inheritdoc/>
    public EnableRequestRewindMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public async Task Invoke(HttpContext context)
    {
        context.Request.EnableRewind();
        await _next(context);
    }
}

Startup.cs

(วางไว้ที่จุดเริ่มต้นของวิธีกำหนดค่า)

app.UseMiddleware<EnableRequestRewindMiddleware>();

มิดเดิลแวร์อื่น ๆ

นี่เป็นส่วนหนึ่งของมิดเดิลแวร์ที่ต้องมีการคลายข้อมูล POSTed เพื่อตรวจสอบสิ่งต่างๆ

using (var stream = new MemoryStream())
{
    // make sure that body is read from the beginning
    context.Request.Body.Seek(0, SeekOrigin.Begin);
    context.Request.Body.CopyTo(stream);
    string requestBody = Encoding.UTF8.GetString(stream.ToArray());

    // this is required, otherwise model binding will return null
    context.Request.Body.Seek(0, SeekOrigin.Begin);
}

3

เมื่อเร็ว ๆ นี้ฉันได้พบโซลูชันที่ยอดเยี่ยมมากซึ่งใช้ JSON แบบสุ่มซึ่งคุณไม่รู้โครงสร้าง:

    [HttpPost]
    public JsonResult Test([FromBody] JsonElement json)
    {
        return Json(json);
    }

ง่ายแค่นั้นเอง


ขอบคุณมันใช้งานได้จริง ฉันใช้GetRawText()วิธีการของ JsonElement และได้รับข้อความ JSON ของฉัน
mrNone

สิ่งนี้จะไม่ให้เนื้อความคำขอจริงแก่คุณหาก DTO ทำการประมวลผลบางอย่างเมื่อคุณสร้างขึ้นเช่นการตั้งค่าเริ่มต้นหรือบางอย่าง
Ebram Shehata

2

สำหรับการอ่านBodyคุณสามารถอ่านแบบอะซิงโครนัสได้

ใช้asyncวิธีการดังต่อไปนี้:

public async Task<IActionResult> GetBody()
{
      string body="";
      using (StreamReader stream = new StreamReader(Request.Body))
      {
           body = await stream.ReadToEndAsync();
      }
    return Json(body);
}

ทดสอบกับบุรุษไปรษณีย์:

ใส่คำอธิบายภาพที่นี่

มันทำงานได้ดีและผ่านการทดสอบในรุ่นAsp.net core2.0 , 2.1 , 2.2, 3.0

ฉันหวังว่าจะเป็นประโยชน์


1

IHttpContextAccessorวิธีการทำงานถ้าคุณต้องการที่จะไปเส้นทางนี้

TLDR;

  • ฉีด IHttpContextAccessor

  • กรอกลับ - HttpContextAccessor.HttpContext.Request.Body.Seek(0, System.IO.SeekOrigin.Begin);

  • อ่าน - System.IO.StreamReader sr = new System.IO.StreamReader(HttpContextAccessor.HttpContext.Request.Body); JObject asObj = JObject.Parse(sr.ReadToEnd());

เพิ่มเติม - ความพยายามในการรวบรัดและไม่รวบรวมตัวอย่างของรายการที่คุณจะต้องตรวจสอบให้แน่ใจว่าอยู่ในสถานที่เพื่อให้สามารถใช้งานIHttpContextAccessorได้ คำตอบได้ชี้ให้เห็นอย่างถูกต้องว่าคุณจะต้องย้อนกลับไปที่จุดเริ่มต้นเมื่อคุณพยายามอ่านเนื้อหาของคำขอ CanSeek, Positionคุณสมบัติในการสตรีมคำขอร่างกายที่เป็นประโยชน์ในการตรวจสอบนี้

.NET Core DI เอกสาร

// First -- Make the accessor DI available
//
// Add an IHttpContextAccessor to your ConfigureServices method, found by default
// in your Startup.cs file:
// Extraneous junk removed for some brevity:
public void ConfigureServices(IServiceCollection services)
{
    // Typical items found in ConfigureServices:
    services.AddMvc(config => { config.Filters.Add(typeof(ExceptionFilterAttribute)); });
    // ...

    // Add or ensure that an IHttpContextAccessor is available within your Dependency Injection container
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}

// Second -- Inject the accessor
//
// Elsewhere in the constructor of a class in which you want
// to access the incoming Http request, typically 
// in a controller class of yours:
public class MyResourceController : Controller
{
    public ILogger<PricesController> Logger { get; }
    public IHttpContextAccessor HttpContextAccessor { get; }

    public CommandController(
        ILogger<CommandController> logger,
        IHttpContextAccessor httpContextAccessor)
    {
        Logger = logger;
        HttpContextAccessor = httpContextAccessor;
    }

    // ...

    // Lastly -- a typical use 
    [Route("command/resource-a/{id}")]
    [HttpPut]
    public ObjectResult PutUpdate([FromRoute] string id, [FromBody] ModelObject requestModel)
    {
        if (HttpContextAccessor.HttpContext.Request.Body.CanSeek)
        {
            HttpContextAccessor.HttpContext.Request.Body.Seek(0, System.IO.SeekOrigin.Begin);
            System.IO.StreamReader sr = new System.IO.StreamReader(HttpContextAccessor.HttpContext.Request.Body);
            JObject asObj = JObject.Parse(sr.ReadToEnd());

            var keyVal = asObj.ContainsKey("key-a");
        }
    }
}    

1

ฉันสามารถอ่านเนื้อหาคำขอในแอปพลิเคชัน asp.net core 3.1 ได้เช่นนี้ (ร่วมกับมิดเดิลแวร์ธรรมดาที่เปิดใช้งานการย้อนกลับที่เปิดใช้งานบัฟเฟอร์ได้ดูเหมือนว่าจะใช้งานได้กับ. Net Core เวอร์ชันก่อนหน้า -):

var reader = await Request.BodyReader.ReadAsync();
Request.Body.Position = 0;
var buffer = reader.Buffer;
var body = Encoding.UTF8.GetString(buffer.FirstSpan);
Request.Body.Position = 0;

0

ฉันต้องการอ่านคำร้องขอด้วยโดยไม่ต้องแมปมันกับโมเดลพารามิเตอร์การดำเนินการบางอย่างโดยอัตโนมัติ ทดสอบวิธีต่างๆมากมายก่อนที่จะแก้ไขปัญหานี้ และฉันไม่พบวิธีการทำงานใด ๆ ที่อธิบายไว้ที่นี่ ปัจจุบันโซลูชันนี้ใช้. NET Core 3.0 framework

reader.readToEnd () seamed เป็นวิธีง่ายๆแม้ว่าจะคอมไพล์ แต่ก็มีข้อยกเว้นรันไทม์ทำให้ฉันต้องใช้การโทรแบบ async ดังนั้นฉันจึงใช้ ReadToEndAsync () แทน แต่มันใช้งานได้ในบางครั้งและบางครั้งก็ไม่ได้ แจ้งข้อผิดพลาดเช่นไม่สามารถอ่านได้หลังจากปิดสตรีมแล้ว ปัญหาคือเราไม่สามารถรับประกันได้ว่าจะส่งคืนผลลัพธ์ในเธรดเดียวกัน (แม้ว่าเราจะใช้ await) ดังนั้นเราจึงต้องการการติดต่อกลับบางอย่าง วิธีนี้ใช้ได้ผลสำหรับฉัน

[Route("[controller]/[action]")]
public class MyController : ControllerBase
{

    // ...

    [HttpPost]
    public async void TheAction()
    {
        try
        {
            HttpContext.Request.EnableBuffering();
            Request.Body.Position = 0;
            using (StreamReader stream = new StreamReader(HttpContext.Request.Body))
            {
                var task = stream
                    .ReadToEndAsync()
                    .ContinueWith(t => {
                        var res = t.Result;
                        // TODO: Handle the post result!
                    });

                // await processing of the result
                task.Wait();
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to handle post!");
        }
    }

0

วิธีที่ง่ายที่สุดในการดำเนินการมีดังต่อไปนี้:

  1. ในเมธอด Controller ที่คุณต้องการแยกเนื้อหาให้เพิ่มพารามิเตอร์นี้: ค่า [FromBody] SomeClass

  2. ประกาศ "SomeClass" เป็นคลาส SomeClass {สตริงสาธารณะ SomeParameter {get; ชุด; }}

เมื่อเนื้อความดิบถูกส่งเป็น json แกน. net จะรู้วิธีอ่านได้ง่ายมาก


0

สำหรับผู้ที่ต้องการรับเนื้อหา (เนื้อหาคำขอ) จากคำขอ:

ใช้[FromBody]แอตทริบิวต์ในพารามิเตอร์วิธีการควบคุมของคุณ

[Route("api/mytest")]
[ApiController]
public class MyTestController : Controller
{
    [HttpPost]
    [Route("content")]
    public async Task<string> ReceiveContent([FromBody] string content)
    {
        // Do work with content
    }
}

ดังที่ doc กล่าวว่า: แอตทริบิวต์นี้ระบุว่าพารามิเตอร์หรือคุณสมบัติควรถูกผูกไว้โดยใช้เนื้อความของคำขอ


0

นี่เป็นวิธีแก้ปัญหาสำหรับPOSTed JSONbody ที่ไม่ต้องใช้มิดเดิลแวร์หรือส่วนขยายใด ๆ สิ่งที่คุณต้องมีคือการลบล้างOnActionExecutingเพื่อให้สามารถเข้าถึงข้อมูลทั้งหมดที่ตั้งค่าในเนื้อหาหรือแม้แต่อาร์กิวเมนต์ใน URL:

using System.Text.Json;

....

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    base.OnActionExecuting(filterContext);
    
    // You can simply use filterContext.ActionArguments to get whatever param that you have set in the action
    // For instance you can get the "json" param like this: filterContext.ActionArguments["json"]
    // Or better yet just loop through the arguments and find the type
    foreach(var elem in filterContext.ActionArguments)
    {
        if(elem.Value is JsonElement)
        {
            // Convert json obj to string
            var json = ((JsonElement)elem.Value).GetRawText();
            break;
        }
    }
}

[HttpPost]
public IActionResult Add([FromBody] JsonElement json, string id = 1)
{
    return Ok("v1");
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.