ทำไมต้องใช้ HttpClient สำหรับการเชื่อมต่อแบบซิงโครนัส


188

ฉันกำลังสร้างไลบรารีคลาสเพื่อโต้ตอบกับ API ฉันต้องเรียก API และประมวลผลการตอบสนอง XML ผมสามารถมองเห็นประโยชน์ของการใช้HttpClientสำหรับการเชื่อมต่อไม่ตรงกัน แต่สิ่งที่ฉันกำลังทำคือการซิงโครหมดจดดังนั้นฉันไม่สามารถเห็นประโยชน์ใด ๆ HttpWebRequestที่สำคัญมากกว่าการใช้

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


3
ฉันเกลียดที่จะบอกคุณ แต่การโทรผ่าน HTTP จะไม่ซิงโครไนซ์อย่างหมดจดเนื่องจากวิธีการทำงานของเครือข่าย windows ภายใน (aka พอร์ตที่เสร็จสมบูรณ์)
TomTom


นอกจากนี้ยังเป็นการดีที่จะรู้ว่า - ใช้ async / คอยอย่างมีประสิทธิภาพกับ ASP.NET Web API
RBT

คำตอบ:


374

แต่สิ่งที่ฉันทำคือการซิงโครนัสอย่างหมดจด

คุณสามารถใช้HttpClientสำหรับคำขอซิงโครนัสได้ดี:

using (var client = new HttpClient())
{
    var response = client.GetAsync("http://google.com").Result;

    if (response.IsSuccessStatusCode)
    {
        var responseContent = response.Content; 

        // by calling .Result you are synchronously reading the result
        string responseString = responseContent.ReadAsStringAsync().Result;

        Console.WriteLine(responseString);
    }
}

เท่าที่ทำไมคุณควรใช้HttpClientมากกว่าWebRequestเป็นห่วง, HttpClientเป็นเด็กใหม่ในบล็อกและอาจมีการปรับปรุงมากกว่าลูกค้าเก่า


27
การใช้วิธีการแบบซิงโครนัสของคุณไม่สามารถบล็อกเธรด UI ของคุณหรือไม่ คุณอาจต้องการพิจารณาอะไรทำนองนั้นstring responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;แทนหากคุณต้องทำแบบซิงโครนัส
ดิน

13
@earthling, ใช่, Task.Runเรียกใช้งานจาก ThreadPool แต่คุณกำลังเรียก.Resultมันว่าฆ่าผลประโยชน์ทั้งหมดจากสิ่งนี้และบล็อกเธรดที่คุณเรียกสิ่งนี้.Result(ซึ่งมักจะเกิดขึ้นเป็นเธรด UI หลัก)
ดารินดิมิททรอฟ

35
ตามโพสต์นี้ ( blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx ) การเรียก.Resultแบบนี้อาจทำให้เธรดพูลหมดและทำให้เกิดการหยุดชะงัก
Pete Garafano

16
รหัสนี้จะหยุดชะงักเสมอหากดำเนินการภายในงานที่สร้างขึ้นบนเธรด UI หลักโดยnew TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()
Wim Coenen

24
ดังนั้นฉันจะใช้ HttpClient พร้อมกันจากด้าย UI ได้อย่างไร สมมติว่าผมจงใจต้องการบล็อกหัวข้อ UI (หรือฉันจะเขียนแอพพลิเคคอนโซล) จนกว่าฉันจะได้รับการตอบสนอง HTTP ... ดังนั้นถ้ารอ () ผล () ฯลฯ อาจทำให้เกิดการติดตายสิ่งที่เป็นวิธีการแก้ปัญหาที่ชัดเจนสำหรับการนี้โดยไม่ต้องความเสี่ยงของการหยุดชะงักและไม่ใช้คลาสอื่นเช่น WebClient หรือไม่
เด็กซ์เตอร์

26

ฉันจะทำซ้ำคำตอบ Donny V. และของ Josh อีกครั้ง

"เหตุผลเดียวที่ฉันไม่ใช้เวอร์ชัน async คือถ้าฉันพยายามสนับสนุน. NET เวอร์ชั่นเก่าที่ไม่ได้มีอยู่แล้วในการรองรับ async"

(และโหวตขึ้นถ้าฉันมีชื่อเสียง)

ฉันจำไม่ได้ว่าครั้งสุดท้ายถ้าฉันรู้สึกขอบคุณจริง ๆ ที่ HttpWebRequest ขว้างข้อยกเว้นสำหรับรหัสสถานะ> = 400 เพื่อแก้ไขปัญหาเหล่านี้คุณต้องรับการยกเว้นทันทีและแมปกับกลไกการตอบสนองที่ไม่ใช่ข้อยกเว้น ในรหัสของคุณ ... น่าเบื่อน่าเบื่อและมีข้อผิดพลาดเกิดขึ้นเอง ไม่ว่าจะเป็นการสื่อสารกับฐานข้อมูลหรือการใช้เว็บพรอกซี bespoke มันเป็นที่ต้องการเกือบทุกครั้งที่ไดรเวอร์ Http บอกรหัสแอปพลิเคชันของคุณว่าอะไรถูกส่งคืนและปล่อยให้คุณตัดสินใจว่าจะทำอย่างไร

ดังนั้น HttpClient จะดีกว่า


1
ฉันรู้สึกประหลาดใจที่HttpClientตัวเองเป็นเสื้อคลุมรอบ ๆHttpWebRequest(ที่จริง ๆ แล้วจับภายในWebExceptionวัตถุเหล่านั้นและทำการแปลงให้เป็นHttpResponseMessageสำหรับคุณ) ฉันคิดว่ามันจะง่ายกว่าถ้าจะสร้างลูกค้าใหม่ทั้งหมดตั้งแต่เริ่มต้น
ได

4
มีเหตุผลที่ดีมากมายที่ไม่ต้องการเขียนโค้ดทั้งหมดของคุณอีกครั้งสำหรับการโทร http ระดับต่ำที่ไม่ได้มีประสิทธิภาพที่สำคัญ (แต่จะแนะนำ async ให้กับหลายล้านแห่ง)
FrankyBoy

ใน. net core 2 หากคุณต้องการประเมินนิพจน์ด้วย DynamicExpressionParser แบบไดนามิกอาจไม่สามารถใช้ async ได้ ตัวสร้างดัชนีคุณสมบัติไม่สามารถใช้ async ได้ ในสถานการณ์ของฉันฉันต้องการที่จะประเมินผลแบบไดนามิกสตริงเช่น "GetDefaultWelcomeMessage [\" InitialMessage \ "]" ซึ่งวิธีการนี้ทำให้ HttpCall และไวยากรณ์ดัชนีเป็นที่นิยมวิธีไวยากรณ์ "Util.GetDefaultWelcomeMessage (\" InitialMessage \ ") ที่"
Eugen

7

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

คุณไม่ทราบว่าจะใช้ห้องสมุดของคุณอย่างไร บางทีผู้ใช้อาจกำลังประมวลผลคำขอจำนวนมากและการดำเนินการแบบอะซิงโครนัสจะช่วยให้ทำงานได้เร็วขึ้นและมีประสิทธิภาพมากขึ้น

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

เหตุผลเดียวที่ฉันจะไม่ใช้เวอร์ชัน async คือถ้าฉันพยายามสนับสนุน. NET เวอร์ชั่นเก่าที่ไม่ได้มีอยู่แล้วในการสนับสนุน async


ฉันเห็นแล้วทำไลบรารีคลาสแบบอะซิงโครนัสและอนุญาตให้ผู้ใช้ระบบตัดสินใจว่าจะใช้ async หรือใช้รอและใช้พร้อมกันหรือไม่
ซอสมะเขือเทศ

ถัดไปรอช่วยในการโทรบางอย่างไม่ตรงกันโดยกลับการควบคุมไปยังผู้โทร
Josh Smeaton

6

ในกรณีของฉันคำตอบที่ยอมรับไม่ทำงาน ฉันกำลังเรียก API จากแอปพลิเคชัน MVC ซึ่งไม่มีการกระทำแบบอะซิงโครนัส

นี่คือวิธีที่ฉันจัดการเพื่อให้ทำงาน:

private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static T RunSync<T>(Func<Task<T>> func)
    {           
        CultureInfo cultureUi = CultureInfo.CurrentUICulture;
        CultureInfo culture = CultureInfo.CurrentCulture;
        return _myTaskFactory.StartNew<Task<T>>(delegate
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap<T>().GetAwaiter().GetResult();
    }

จากนั้นฉันก็เรียกมันว่า:

Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));

1
ขอบคุณ @Darkonekt ... มันทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน เฉพาะ HttpClient.SendAsync (... ) ผลลัพธ์จะไม่ทำงานใน AspNet Handler (.ASHX)
Rafael Kazuo Sato Simiao

3
public static class AsyncHelper  
{
    private static readonly TaskFactory _taskFactory = new
        TaskFactory(CancellationToken.None,
                    TaskCreationOptions.None,
                    TaskContinuationOptions.None,
                    TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        => _taskFactory
            .StartNew(func)
            .Unwrap()
            .GetAwaiter()
            .GetResult();

    public static void RunSync(Func<Task> func)
        => _taskFactory
            .StartNew(func)
            .Unwrap()
            .GetAwaiter()
            .GetResult();
}

แล้วก็

AsyncHelper.RunSync(() => DoAsyncStuff());

ถ้าคุณใช้คลาสนั้นผ่านวิธีการ async ของคุณเป็นพารามิเตอร์คุณสามารถเรียกวิธีการแบบ async จากวิธีการซิงค์ได้อย่างปลอดภัย

มันอธิบายไว้ที่นี่: https://cpratt.co/async-tips-tricks/


-1

คำตอบทั้งหมดดูเหมือนจะมุ่งเน้นไปที่การใช้แบบHttpClientซิงโครนัสแทนที่จะให้คำตอบสำหรับคำถามที่แท้จริง

HttpClientเป็นมากกว่าตัวจัดการการร้องขอ / ตอบกลับที่ง่าย ๆ เพื่อให้สามารถจัดการกับลักษณะเฉพาะของเครือข่ายที่แตกต่างกันได้ ในกรณีของฉันคือการทำงานกับพร็อกซี NTLM ซึ่งต้องมีการเจรจาส่งคำขอ / ตอบกลับพร้อมโทเค็นและข้อมูลประจำตัวระหว่างไคลเอนต์และพร็อกซีเซิร์ฟเวอร์เพื่อตรวจสอบสิทธิ์หลายรายการ HttpClient(ใช้HttpClientHandler) ดูเหมือนว่าจะมีกลไกในตัวที่จัดการที่ส่งคืนทรัพยากรนอกเหนือจากพร็อกซีด้วยการเรียกใช้เมธอดเดียว


คำตอบของคุณไม่ได้อธิบายวิธีการใช้ HttpClient แบบอะซิงโครนัส
user275801

@ user275801 นั่นคือความคิดเห็นที่เป็นใบ้ ไม่มีใครถามว่า มันไม่ตรงกันโดยค่าเริ่มต้น
Bizniztime
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.