การตัดสินใจระหว่าง HttpClient และ WebClient


218

เว็บแอปของเรากำลังทำงานใน. Net Framework 4.0 UI เรียกวิธีควบคุมผ่านการเรียก ajax

เราจำเป็นต้องใช้บริการ REST จากผู้ขายของเรา ฉันกำลังประเมินวิธีที่ดีที่สุดในการเรียกใช้บริการ REST ใน. Net 4.0 เซอร์วิส REST ต้องการ Basic Authentication Scheme และสามารถส่งคืนข้อมูลทั้งใน XML และ JSON ไม่มีข้อกำหนดสำหรับการอัปโหลด / ดาวน์โหลดข้อมูลขนาดใหญ่และฉันไม่เห็นอะไรเลยในอนาคต ฉันดูโครงการโอเพนซอร์สสองสามโครงการสำหรับการใช้ REST และไม่พบค่าใด ๆ ในการพิสูจน์การพึ่งพาเพิ่มเติมในโครงการ เริ่มต้นในการประเมินและWebClient HttpClientฉันดาวน์โหลด HttpClient สำหรับ. Net 4.0 จาก NuGet

ผมค้นหาความแตกต่างระหว่างWebClientและHttpClientและเว็บไซต์นี้บอกว่าเดียว HttpClient สามารถจัดการกับสายพร้อมกันและสามารถใช้การแก้ไข DNS, การตั้งค่าคุกกี้และการตรวจสอบ ฉันยังไม่เห็นค่าจริงที่เราอาจได้รับเนื่องจากความแตกต่าง

ฉันทำการทดสอบประสิทธิภาพอย่างรวดเร็วเพื่อค้นหาว่าWebClient(การเรียกสาย), HttpClient(ซิงค์และ async) ทำงานอย่างไร และนี่คือผลลัพธ์:

ใช้HttpClientอินสแตนซ์เดียวกันสำหรับคำขอทั้งหมด (ขั้นต่ำ - สูงสุด)

การซิงค์ WebClient: 8 ms - 167 ms
ซิงค์ HttpClient: 3 ms - 7228 ms
astt HttpClient: 985 - 10405 ms

ใช้ใหม่HttpClientสำหรับแต่ละคำขอ (ต่ำสุด - สูงสุด)

การซิงค์ WebClient: 4 ms - 297 ms
ซิงค์ HttpClient: 3 ms - 7953 ms
HttpClient async: 1027 - 10834 ms

รหัส

public class AHNData
{
    public int i;
    public string str;
}

public class Program
{
    public static HttpClient httpClient = new HttpClient();
    private static readonly string _url = "http://localhost:9000/api/values/";

    public static void Main(string[] args)
    {
       #region "Trace"
       Trace.Listeners.Clear();

       TextWriterTraceListener twtl = new TextWriterTraceListener(
           "C:\\Temp\\REST_Test.txt");
       twtl.Name = "TextLogger";
       twtl.TraceOutputOptions = TraceOptions.ThreadId | TraceOptions.DateTime;

       ConsoleTraceListener ctl = new ConsoleTraceListener(false);
       ctl.TraceOutputOptions = TraceOptions.DateTime;

       Trace.Listeners.Add(twtl);
       Trace.Listeners.Add(ctl);
       Trace.AutoFlush = true;
       #endregion

       int batchSize = 1000;

       ParallelOptions parallelOptions = new ParallelOptions();
       parallelOptions.MaxDegreeOfParallelism = batchSize;

       ServicePointManager.DefaultConnectionLimit = 1000000;

       Parallel.For(0, batchSize, parallelOptions,
           j =>
           {
               Stopwatch sw1 = Stopwatch.StartNew();
               GetDataFromHttpClientAsync<List<AHNData>>(sw1);
           });
       Parallel.For(0, batchSize, parallelOptions,
            j =>
            {
                Stopwatch sw1 = Stopwatch.StartNew();
                GetDataFromHttpClientSync<List<AHNData>>(sw1);
            });
       Parallel.For(0, batchSize, parallelOptions,
            j =>
            {
                using (WebClient client = new WebClient())
                {
                   Stopwatch sw = Stopwatch.StartNew();
                   byte[] arr = client.DownloadData(_url);
                   sw.Stop();

                   Trace.WriteLine("WebClient Sync " + sw.ElapsedMilliseconds);
                }
           });

           Console.Read();
        }

        public static T GetDataFromWebClient<T>()
        {
            using (var webClient = new WebClient())
            {
                webClient.BaseAddress = _url;
                return JsonConvert.DeserializeObject<T>(
                    webClient.DownloadString(_url));
            }
        }

        public static void GetDataFromHttpClientSync<T>(Stopwatch sw)
        {
            HttpClient httpClient = new HttpClient();
            var response = httpClient.GetAsync(_url).Result;
            var obj = JsonConvert.DeserializeObject<T>(
                response.Content.ReadAsStringAsync().Result);
            sw.Stop();

            Trace.WriteLine("HttpClient Sync " + sw.ElapsedMilliseconds);
        }

        public static void GetDataFromHttpClientAsync<T>(Stopwatch sw)
        {
           HttpClient httpClient = new HttpClient();
           var response = httpClient.GetAsync(_url).ContinueWith(
              (a) => {
                 JsonConvert.DeserializeObject<T>(
                    a.Result.Content.ReadAsStringAsync().Result);
                 sw.Stop();
                 Trace.WriteLine("HttpClient Async " + sw.ElapsedMilliseconds);
              }, TaskContinuationOptions.None);
        }
    }
}

คำถามของฉัน

  1. การเรียกส่วนที่เหลือส่งคืนใน 3-4s ซึ่งเป็นที่ยอมรับ เซอร์วิส Call to REST เริ่มต้นในวิธีการควบคุมซึ่งเรียกใช้จากการโทร ajax ในการเริ่มต้นการโทรจะทำงานในเธรดอื่นและไม่บล็อก UI ดังนั้นฉันสามารถเพียงแค่ติดกับสายซิงค์?
  2. รหัสข้างต้นถูกเรียกใช้ในกล่องภายในเครื่องของฉัน ในการตั้งค่าผลิตภัณฑ์จะทำการค้นหา DNS และพร็อกซี มีข้อได้เปรียบในการใช้งานหรือไม่HttpClientมากกว่าWebClient?
  3. การทำงานHttpClientพร้อมกันดีกว่าWebClientหรือไม่ จากผลการทดสอบฉันเห็นว่าการ WebClientซิงค์มีประสิทธิภาพดีกว่า
  4. จะHttpClientเป็นตัวเลือกการออกแบบที่ดีกว่าถ้าเราอัพเกรดเป็น. Net 4.5 ประสิทธิภาพเป็นปัจจัยสำคัญในการออกแบบ

5
การทดสอบของคุณไม่ยุติธรรมGetDataFromHttpClientAsyncเนื่องจากการดำเนินการครั้งแรกการเรียกใช้อื่น ๆ จะได้รับประโยชน์จากข้อมูลที่อาจเกิดขึ้น (ไม่ว่าจะเป็นบนเครื่องโลคัลหรือพร็อกซีที่โปร่งใสระหว่างคุณกับปลายทาง) และจะเร็วขึ้น นอกจากนี้ภายใต้เงื่อนไขที่ถูกต้องvar response = httpClient.GetAsync("http://localhost:9000/api/values/").Result;อาจส่งผลให้เกิดการหยุดชะงักเนื่องจากเธรดเธรดพูลของคุณหมดลง คุณไม่ควรบล็อกกิจกรรมที่ขึ้นอยู่กับเธรดพูลในเธรด ThreadPool คุณควรawaitกลับไปที่เธรดกลับไปที่พูลแทน
Scott Chamberlain

1
HttpClient พร้อม Web API Client นั้นยอดเยี่ยมสำหรับไคลเอ็นต์ JSON / XML REST
คอรีเนลสัน

@Scott Chamberlain - ขอบคุณสำหรับการตอบกลับของคุณ เนื่องจากการทดสอบทั้งหมดเรียกใช้ใน Parallel.Foreach ไม่มีการรับประกันว่าจะเรียกใช้ก่อน นอกจากนี้หากมีการเรียกใช้บริการครั้งแรกจาก GetDataFromHttpClientAsync การโทรที่ตามมาทั้งหมดจาก GetDataFromHttpClientAsync ควรได้รับประโยชน์จากแคชและทำงานได้เร็วขึ้น ฉันไม่เห็นสิ่งนั้นในผลลัพธ์ รอ Rgd เรายังคงใช้ 4.0 ฉันเห็นด้วยกับคุณว่า HttpClient ในแบบซิงค์จะนำไปสู่การหยุดชะงักและฉันจะพิจารณาตัวเลือกนั้นจากการพิจารณาการออกแบบของฉัน
user3092913

@CoryNelson คุณช่วยอธิบายเพิ่มเติมได้ไหมว่าทำไม HttpClient กับ Web API Client นั้นยอดเยี่ยมสำหรับไคลเอ็นต์ JSON / XML REST
user3092913

2
ต่อไปนี้เป็นคำบางคำเกี่ยวกับความแตกต่างระหว่าง HttpClient และ WebClient: blogs.msdn.com/b/henrikn/archive/2012/02/11/…
JustAndrei

คำตอบ:


243

ฉันอาศัยอยู่ทั้งในโลก F # และ Web API

มีสิ่งดีๆมากมายเกิดขึ้นกับ Web API โดยเฉพาะในรูปแบบของเครื่องมือจัดการข้อความเพื่อความปลอดภัยเป็นต้น

ฉันรู้ว่าของฉันเป็นเพียงความคิดเห็นเดียว แต่ฉันจะแนะนำให้ใช้HttpClientกับงานในอนาคตเท่านั้น อาจมีวิธีที่จะยกระดับชิ้นส่วนอื่น ๆ ที่ออกมาSystem.Net.Httpโดยไม่ใช้ชุดประกอบนั้นโดยตรง แต่ฉันไม่สามารถจินตนาการได้ว่าจะใช้งานได้อย่างไรในเวลานี้

พูดถึงการเปรียบเทียบสองสิ่งนี้

  • HttpClient นั้นใกล้เคียงกับ HTTP มากกว่า WebClient
  • HttpClient ไม่ได้หมายถึงการแทนที่เว็บไคลเอ็นต์อย่างสมบูรณ์เนื่องจากมีสิ่งต่าง ๆ เช่นความคืบหน้าของรายงานรูปแบบ URI ที่กำหนดเองและการโทร FTP ที่ WebClient จัดเตรียมไว้ให้ แต่ HttpClient ไม่ได้
+--------------------------------------------+--------------------------------------------+
|               WebClient                    |               HttpClient                   |
+--------------------------------------------+--------------------------------------------+
| Available in older versions of .NET        | .NET 4.5 only.  Created to support the     |
|                                            | growing need of the Web API REST calls     |
+--------------------------------------------+--------------------------------------------+
| WinRT applications cannot use WebClient    | HTTPClient can be used with WinRT          |
+--------------------------------------------+--------------------------------------------+
| Provides progress reporting for downloads  | No progress reporting for downloads        |
+--------------------------------------------+--------------------------------------------+
| Does not reuse resolved DNS,               | Can reuse resolved DNS, cookie             |
| configured cookies                         | configuration and other authentication     |
+--------------------------------------------+--------------------------------------------+
| You need to new up a WebClient to          | Single HttpClient can make concurrent      |
| make concurrent requests.                  | requests                                   |
+--------------------------------------------+--------------------------------------------+
| Thin layer over WebRequest and             | Thin layer of HttpWebRequest and           |
| WebResponse                                | HttpWebResponse                            |
+--------------------------------------------+--------------------------------------------+
| Mocking and testing WebClient is difficult | Mocking and testing HttpClient is easy     |
+--------------------------------------------+--------------------------------------------+
| Supports FTP                               | No support for FTP                         |
+--------------------------------------------+--------------------------------------------+
| Both Synchronous and Asynchronous methods  | All IO bound methods in                    |
| are available for IO bound requests        | HTTPClient are asynchronous                |
+--------------------------------------------+--------------------------------------------+

หากคุณใช้. NET 4.5 โปรดใช้ async goodness กับ HttpClient ที่ Microsoft มอบให้กับนักพัฒนา HttpClient นั้นมีความสมมาตรอย่างมากต่อพี่น้องในฝั่งเซิร์ฟเวอร์ของ HTTP ซึ่งเป็น HttpRequest และ HttpResponse

อัปเดต: 5 เหตุผลในการใช้ HttpClient API ใหม่:

  • ส่วนหัวที่พิมพ์อย่างมาก
  • แคชที่ใช้ร่วมกันคุกกี้และข้อมูลรับรอง
  • เข้าถึงคุกกี้และคุกกี้ที่แชร์
  • ควบคุมการแคชและแคชที่แชร์
  • ฉีดโมดูลรหัสของคุณลงในไปป์ไลน์ ASP.NET รหัสทำความสะอาดและโมดูลาร์

การอ้างอิง

C # 5.0 Joseph Albahari

(ช่อง 9 - สร้างวิดีโอ 2013)

ห้าเหตุผลสำคัญในการใช้ HttpClient API ใหม่เพื่อเชื่อมต่อกับบริการเว็บ

WebClient vs HttpClient vs HttpWebRequest


4
ควรกล่าวถึงว่า HttpClient พร้อมใช้งานสำหรับ. NET 4.0เช่นกัน
ทอดด์ Menier

2
สิ่งนี้ไม่ได้อธิบายว่าเพราะเหตุใด WebClient จึงมีขนาดที่ใหญ่กว่า HttpClient นอกจากนี้ยังWebClientดูเหมือนว่าจะมีวิธีการ async ในขณะนี้
บดขยี้

8
@ crush เป็นเพราะ OP กำลังสร้างอินสแตนซ์ใหม่ของ HttpClient สำหรับทุกคำขอ คุณควรใช้ HttpClient อินสแตนซ์เดียวแทนตลอดอายุการใช้งานของแอปพลิเคชันของคุณ ดูstackoverflow.com/a/22561368/57369
Gabriel

6
มันเป็นที่น่าสังเกตว่าWebClientไม่สามารถใช้ได้ใน.Net Coreแต่HttpClientเป็น
ซิงห์

3
ตั้งแต่. Net Core 2.0 WebClient (หนึ่งในพันของ API อื่น ๆ ) กลับมาพร้อมใช้งาน
CoderBang

56

HttpClient เป็น API ที่ใหม่กว่าและมีประโยชน์

  • มีรูปแบบการเขียนโปรแกรม async ที่ดี
  • ทำงานโดย Henrik F Nielson ซึ่งเป็นหนึ่งในนักประดิษฐ์ของ HTTP และเขาออกแบบ API ดังนั้นจึงเป็นเรื่องง่ายสำหรับคุณที่จะปฏิบัติตามมาตรฐาน HTTP เช่นการสร้างส่วนหัวที่สอดคล้องกับมาตรฐาน
  • อยู่ในกรอบงาน. Net 4.5 ดังนั้นจึงมีการรับประกันระดับการสนับสนุนสำหรับอนาคตอันใกล้
  • นอกจากนี้ยังมีไลบรารี่รุ่น xcopyable / portable-framework ของไลบรารี่ถ้าคุณต้องการใช้กับแพลตฟอร์มอื่น - .Net 4.0, Windows Phone เป็นต้น

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

หมายเหตุ: มันไม่ได้เป็น AFAIK ที่มีประสิทธิภาพมากกว่า มันอาจจะคล้าย ๆ กันถ้าคุณสร้างการทดสอบที่ยุติธรรม


ถ้ามันมีวิธีที่จะเปลี่ยนพร็อกซีมันจะบ้า
ed22

3

ประการแรกฉันไม่ได้เป็นผู้มีอำนาจใน WebClient vs. HttpClient โดยเฉพาะ ประการที่สองจากความคิดเห็นของคุณด้านบนดูเหมือนว่าจะแนะนำว่า WebClient ซิงค์เฉพาะในขณะที่ HttpClient เป็นทั้งคู่

ฉันทำการทดสอบประสิทธิภาพอย่างรวดเร็วเพื่อค้นหาว่า WebClient (ซิงค์สาย), HttpClient (Sync และ Async) ทำงานอย่างไร และนี่คือผลลัพธ์

ฉันเห็นว่าเป็นความแตกต่างอย่างมากเมื่อคิดถึงอนาคตเช่นกระบวนการที่ใช้เวลานาน GUI ตอบสนอง ฯลฯ (เพิ่มเพื่อผลประโยชน์ที่คุณแนะนำโดยกรอบ 4.5 - ซึ่งในประสบการณ์จริงของฉันเร็วขึ้นอย่างมากใน IIS)


4
WebClientดูเหมือนจะมีความสามารถในการ async ใน. NET รุ่นล่าสุด ฉันต้องการที่จะรู้ว่าทำไมมันดูเหมือนจะมีประสิทธิภาพสูงกว่า HttpClient ในขนาดใหญ่เช่นนี้
สนใจ

1
ตามstackoverflow.com/a/4988325/1662973ดูเหมือนว่าจะเหมือนกันนอกเหนือจากข้อเท็จจริงที่ว่าสิ่งหนึ่งเป็นสิ่งที่เป็นนามธรรม อาจขึ้นอยู่กับวิธีการใช้ / โหลดของวัตถุ เวลาต่ำสุดรองรับคำสั่งที่จริงแล้ว webclient เป็นนามธรรมของ HttpClient ดังนั้นจึงมีค่าใช้จ่ายเป็นมิลลิวินาที กรอบอาจเป็น "ลับ ๆ ล่อๆ" ในวิธีการรวมกำไรหรือการกำจัดของ webclient จริงๆ
Anthony Horne

3

HttpClientFactory

สิ่งสำคัญคือการประเมินวิธีต่างๆที่คุณสามารถสร้าง HttpClient และส่วนหนึ่งของการทำความเข้าใจ HttpClientFactory

https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests

นี่ไม่ใช่คำตอบโดยตรงที่ฉันรู้ แต่คุณควรเริ่มต้นที่นี่ดีกว่าจบลงด้วยnew HttpClient(...)ทุกที่


2

ฉันมีเกณฑ์มาตรฐานระหว่าง HttpClient, WebClient, HttpWebResponse จากนั้นเรียก Rest Web Api

และส่งผลให้เกณฑ์การเรียกใช้เว็บ Rest Rest Web

--------------------- ด่านที่ 1 ---- 10 คำขอ

{00: 00: 17.2232544} ====> HttpClinet

{00: 00: 04.3108986} ====> WebRequest

{00: 00: 04.5436889} ====> WebClient

--------------------- ด่านที่ 1 ---- 10 คำขอ - ขนาดเล็ก

{00: 00: 17.2232544} ====> HttpClinet

{00: 00: 04,3108986} ====> WebRequest

{00: 00: 04,5436889} ====> WebClient

--------------------- ด่านที่ 3 ---- 10 คำขอซิงค์ - ขนาดเล็ก

{00: 00: 15.3047502} ====> HttpClinet

{00: 00: 03,5505249} ====> WebRequest

{00: 00: 04,0761359} ====> WebClient

--------------------- ด่านที่ 4 ---- คำขอซิงค์ 100 ครั้ง - ขนาดเล็ก

{00: 03: 23.6268086} ====> HttpClinet

{00: 00: 47.1406632} ====> WebRequest

{00: 01: 01,2319499} ====> WebClient

--------------------- ด่านที่ 5 ---- 10 คำขอซิงค์ - ขนาดสูงสุด

{00: 00: 58.1804677} ====> HttpClinet

{00: 00: 58.0710444} ====> WebRequest

{00: 00: 38.4170938} ====> WebClient

--------------------- ด่านที่ 6 ---- 10 คำขอซิงค์ - ขนาดสูงสุด

{00: 01: 04,9964278} ====> HttpClinet

{00: 00: 59.1429764} ====> WebRequest

{00: 00: 32.0584836} ====> WebClient

_____ WebClient เร็วกว่า ()

var stopWatch = new Stopwatch();
        stopWatch.Start();
        for (var i = 0; i < 10; ++i)
        {
            CallGetHttpClient();
            CallPostHttpClient();
        }

        stopWatch.Stop();

        var httpClientValue = stopWatch.Elapsed;

        stopWatch = new Stopwatch();

        stopWatch.Start();
        for (var i = 0; i < 10; ++i)
        {
            CallGetWebRequest();
            CallPostWebRequest();
        }

        stopWatch.Stop();

        var webRequesttValue = stopWatch.Elapsed;


        stopWatch = new Stopwatch();

        stopWatch.Start();
        for (var i = 0; i < 10; ++i)
        {

            CallGetWebClient();
            CallPostWebClient();

        }

        stopWatch.Stop();

        var webClientValue = stopWatch.Elapsed;

//-------------------------ฟังก์ชั่น

private void CallPostHttpClient()
    {
        var httpClient = new HttpClient();
        httpClient.BaseAddress = new Uri("https://localhost:44354/api/test/");
        var responseTask = httpClient.PostAsync("PostJson", null);
        responseTask.Wait();

        var result = responseTask.Result;
        var readTask = result.Content.ReadAsStringAsync().Result;

    }
    private void CallGetHttpClient()
    {
        var httpClient = new HttpClient();
        httpClient.BaseAddress = new Uri("https://localhost:44354/api/test/");
        var responseTask = httpClient.GetAsync("getjson");
        responseTask.Wait();

        var result = responseTask.Result;
        var readTask = result.Content.ReadAsStringAsync().Result;

    }
    private string CallGetWebRequest()
    {
        var request = (HttpWebRequest)WebRequest.Create("https://localhost:44354/api/test/getjson");

        request.Method = "GET";
        request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

        var content = string.Empty;

        using (var response = (HttpWebResponse)request.GetResponse())
        {
            using (var stream = response.GetResponseStream())
            {
                using (var sr = new StreamReader(stream))
                {
                    content = sr.ReadToEnd();
                }
            }
        }

        return content;
    }
    private string CallPostWebRequest()
    {

        var apiUrl = "https://localhost:44354/api/test/PostJson";


        HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(new Uri(apiUrl));
        httpRequest.ContentType = "application/json";
        httpRequest.Method = "POST";
        httpRequest.ContentLength = 0;

        using (var httpResponse = (HttpWebResponse)httpRequest.GetResponse())
        {
            using (Stream stream = httpResponse.GetResponseStream())
            {
                var json = new StreamReader(stream).ReadToEnd();
                return json;
            }
        }

        return "";
    }

    private string CallGetWebClient()
    {
        string apiUrl = "https://localhost:44354/api/test/getjson";


        var client = new WebClient();

        client.Headers["Content-type"] = "application/json";

        client.Encoding = Encoding.UTF8;

        var json = client.DownloadString(apiUrl);


        return json;
    }

    private string CallPostWebClient()
    {
        string apiUrl = "https://localhost:44354/api/test/PostJson";


        var client = new WebClient();

        client.Headers["Content-type"] = "application/json";

        client.Encoding = Encoding.UTF8;

        var json = client.UploadString(apiUrl, "");


        return json;
    }

1
ดูความคิดเห็นของกาเบรียลข้างต้น กล่าวโดยย่อ HttpClient นั้นเร็วกว่ามากหากคุณสร้าง HttpClient หนึ่งอินสแตนซ์และนำมาใช้ใหม่
LT Dan

1

บางทีคุณอาจคิดถึงปัญหาในวิธีที่ต่างออกไป WebClientและHttpClientมีการใช้งานที่แตกต่างกันเป็นหลักในสิ่งเดียวกัน สิ่งที่ฉันแนะนำคือการใช้รูปแบบการฉีดอ้างอิงกับคอนเทนเนอร์ IoCตลอดทั้งแอปพลิเคชันของคุณ คุณควรสร้างส่วนต่อประสานไคลเอ็นต์ที่มีระดับ abstraction สูงกว่าการถ่ายโอน HTTP ระดับต่ำ คุณสามารถเขียนคลาสคอนกรีตที่ใช้ทั้งWebClientและHttpClientแล้วใช้ภาชนะ IoC ฉีดดำเนินการผ่านการตั้งค่า

สิ่งนี้จะช่วยให้คุณสามารถสลับไปมาระหว่างHttpClientและWebClientอย่างง่ายดายเพื่อให้คุณสามารถทดสอบอย่างเป็นกลางในสภาพแวดล้อมการผลิต

ดังนั้นคำถามเช่น:

HttpClient จะเป็นตัวเลือกการออกแบบที่ดีกว่านี้หรือไม่หากเราอัพเกรดเป็น. Net 4.5

สามารถตอบได้อย่างเป็นกลางโดยสลับไปมาระหว่างการใช้งานไคลเอนต์ทั้งสองโดยใช้คอนเทนเนอร์ IoC นี่คืออินเตอร์เฟซตัวอย่างที่คุณอาจขึ้นอยู่กับที่ไม่ได้รวมถึงรายละเอียดใด ๆ เกี่ยวกับหรือHttpClientWebClient

/// <summary>
/// Dependency Injection abstraction for rest clients. 
/// </summary>
public interface IClient
{
    /// <summary>
    /// Adapter for serialization/deserialization of http body data
    /// </summary>
    ISerializationAdapter SerializationAdapter { get; }

    /// <summary>
    /// Sends a strongly typed request to the server and waits for a strongly typed response
    /// </summary>
    /// <typeparam name="TResponseBody">The expected type of the response body</typeparam>
    /// <typeparam name="TRequestBody">The type of the request body if specified</typeparam>
    /// <param name="request">The request that will be translated to a http request</param>
    /// <returns></returns>
    Task<Response<TResponseBody>> SendAsync<TResponseBody, TRequestBody>(Request<TRequestBody> request);

    /// <summary>
    /// Default headers to be sent with http requests
    /// </summary>
    IHeadersCollection DefaultRequestHeaders { get; }

    /// <summary>
    /// Default timeout for http requests
    /// </summary>
    TimeSpan Timeout { get; set; }

    /// <summary>
    /// Base Uri for the client. Any resources specified on requests will be relative to this.
    /// </summary>
    Uri BaseUri { get; set; }

    /// <summary>
    /// Name of the client
    /// </summary>
    string Name { get; }
}

public class Request<TRequestBody>
{
    #region Public Properties
    public IHeadersCollection Headers { get; }
    public Uri Resource { get; set; }
    public HttpRequestMethod HttpRequestMethod { get; set; }
    public TRequestBody Body { get; set; }
    public CancellationToken CancellationToken { get; set; }
    public string CustomHttpRequestMethod { get; set; }
    #endregion

    public Request(Uri resource,
        TRequestBody body,
        IHeadersCollection headers,
        HttpRequestMethod httpRequestMethod,
        IClient client,
        CancellationToken cancellationToken)
    {
        Body = body;
        Headers = headers;
        Resource = resource;
        HttpRequestMethod = httpRequestMethod;
        CancellationToken = cancellationToken;

        if (Headers == null) Headers = new RequestHeadersCollection();

        var defaultRequestHeaders = client?.DefaultRequestHeaders;
        if (defaultRequestHeaders == null) return;

        foreach (var kvp in defaultRequestHeaders)
        {
            Headers.Add(kvp);
        }
    }
}

public abstract class Response<TResponseBody> : Response
{
    #region Public Properties
    public virtual TResponseBody Body { get; }

    #endregion

    #region Constructors
    /// <summary>
    /// Only used for mocking or other inheritance
    /// </summary>
    protected Response() : base()
    {
    }

    protected Response(
    IHeadersCollection headersCollection,
    int statusCode,
    HttpRequestMethod httpRequestMethod,
    byte[] responseData,
    TResponseBody body,
    Uri requestUri
    ) : base(
        headersCollection,
        statusCode,
        httpRequestMethod,
        responseData,
        requestUri)
    {
        Body = body;
    }

    public static implicit operator TResponseBody(Response<TResponseBody> readResult)
    {
        return readResult.Body;
    }
    #endregion
}

public abstract class Response
{
    #region Fields
    private readonly byte[] _responseData;
    #endregion

    #region Public Properties
    public virtual int StatusCode { get; }
    public virtual IHeadersCollection Headers { get; }
    public virtual HttpRequestMethod HttpRequestMethod { get; }
    public abstract bool IsSuccess { get; }
    public virtual Uri RequestUri { get; }
    #endregion

    #region Constructor
    /// <summary>
    /// Only used for mocking or other inheritance
    /// </summary>
    protected Response()
    {
    }

    protected Response
    (
    IHeadersCollection headersCollection,
    int statusCode,
    HttpRequestMethod httpRequestMethod,
    byte[] responseData,
    Uri requestUri
    )
    {
        StatusCode = statusCode;
        Headers = headersCollection;
        HttpRequestMethod = httpRequestMethod;
        RequestUri = requestUri;
        _responseData = responseData;
    }
    #endregion

    #region Public Methods
    public virtual byte[] GetResponseData()
    {
        return _responseData;
    }
    #endregion
}

รหัสเต็ม

การใช้ HttpClient

คุณสามารถใช้Task.Runเพื่อให้WebClientทำงานแบบอะซิงโครนัสในการนำไปใช้งานได้

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


1

ความคิดเห็นไม่เป็นที่นิยมจาก 2020:

เมื่อพูดถึงแอพ ASP.NETฉันยังชอบWebClientมากกว่าHttpClientเพราะ:

  1. การใช้งานที่ทันสมัยมาพร้อมกับวิธีการตามงาน / รอคอย async
  2. มีหน่วยความจำขนาดเล็กลงและเร็วขึ้น 2x-5x (คำตอบอื่น ๆ พูดถึงแล้ว)
  3. ขอแนะนำให้ " ใช้ HttpClient หนึ่งอินสแตนซ์ใหม่ตลอดอายุการใช้งานของแอปพลิเคชันของคุณ " แต่ ASP.NET ไม่มี "อายุการใช้งานของแอปพลิเคชัน" เพียงอายุการใช้งานของการร้องขอเท่านั้น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.