Decydowanie między HttpClient a WebClient

218

Nasza aplikacja internetowa działa w .Net Framework 4.0. Interfejs użytkownika wywołuje metody kontrolera za pośrednictwem wywołań ajax.

Musimy skorzystać z usługi REST od naszego dostawcy. Oceniam najlepszy sposób na wywołanie usługi REST w .Net 4.0. Usługa REST wymaga podstawowego schematu uwierzytelniania i może zwracać dane zarówno w formacie XML, jak i JSON. Nie ma wymogu przesyłania / pobierania dużych danych i nie widzę niczego w przyszłości. Przyjrzałem się kilku projektom z otwartym kodem źródłowym dla zużycia REST i nie znalazłem w nich żadnej wartości uzasadniającej dodatkową zależność w projekcie. Zaczął oceniać WebClienti HttpClient. Pobrałem HttpClient dla .Net 4.0 z NuGet.

Szukałem różnic między WebClienta HttpClienti ta strona wspomniano, że pojedyncze HttpClient mogą obsługiwać połączeń równoczesnych i może ponownego wykorzystania rozwiązany DNS, cookie config i uwierzytelniania. Mam jeszcze do czynienia z praktycznymi wartościami, które możemy zyskać z powodu różnic.

Zrobiłem szybki test wydajności, aby dowiedzieć się, jak działają WebClient(synchronizacja połączeń), HttpClient(synchronizacja i asynchronizacja). a oto wyniki:

Korzystanie z tej samej HttpClientinstancji dla wszystkich żądań (min. - maks.)

Synchronizacja WebClient: 8 ms - 167 ms
HttpClient synchronizacja: 3 ms - 7228 ms
HttpClient asynchronizacja: 985 - 10405 ms

Używanie nowego HttpClientdla każdego żądania (min. - maks.)

Synchronizacja WebClient: 4 ms - 297 ms
HttpClient synchronizacja: 3 ms - 7953 ms
HttpClient asynchroniczna: 1027 - 10834 ms

Kod

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

Moje pytania

  1. Połączenia REST zwracają w ciągu 3-4 sekund, co jest dopuszczalne. Wywołania usługi REST są inicjowane metodami kontrolera, które są wywoływane z wywołań ajax. Na początek wywołania działają w innym wątku i nie blokują interfejsu użytkownika. Czy mogę po prostu trzymać się synchronizacji połączeń?
  2. Powyższy kod został uruchomiony w mojej lokalnej skrzynce. W konfiguracji prod będą zaangażowane wyszukiwanie DNS i proxy. Czy jest jakaś korzyść z używania HttpClientponad WebClient?
  3. Czy HttpClientwspółbieżność jest lepsza niż WebClient? Z wyników testu widzę, że WebClientpołączenia synchroniczne działają lepiej.
  4. Czy HttpClientbędzie lepszy wybór projektu, jeśli uaktualnimy do .Net 4.5? Wydajność jest kluczowym czynnikiem konstrukcyjnym.
użytkownik3092913
źródło
5
Twój test jest niesprawiedliwy, GetDataFromHttpClientAsyncponieważ uruchamia się jako pierwszy, inne wywołania mogą skorzystać z potencjalnego przetwarzania danych (na komputerze lokalnym lub dowolnym przezroczystym serwerze proxy między tobą a miejscem docelowym) i będą szybsze. Również w odpowiednich warunkach var response = httpClient.GetAsync("http://localhost:9000/api/values/").Result;może dojść do impasu z powodu wyczerpania wątków puli wątków. Nigdy nie powinieneś blokować działania zależnego od puli wątków w wątkach ThreadPool, powinieneś awaitzamiast tego zwrócić wątek z powrotem do puli.
Scott Chamberlain,
1
HttpClient z Web API Client jest fantastyczny dla klienta REST JSON / XML.
Cory Nelson,
@Scott Chamberlain - Dziękujemy za odpowiedź. Ponieważ wszystkie wywołania testowe są uruchamiane w trybie równoległym, nie ma gwarancji, które z nich uruchomionoby jako pierwsze. Ponadto, gdyby pierwsze wywołanie usługi było z GetDataFromHttpClientAsync, wszystkie kolejne połączenia z GetDataFromHttpClientAsync powinny skorzystać z pamięci podręcznej i działać szybciej. Nie widziałem tego w wyniku. Rgd czekaj, nadal używamy 4.0. Zgadzam się z Tobą, że synchronizacja HttpClient doprowadziłaby do impasu i wykluczam tę opcję ze względu na mój projekt.
user3092913,
@CoryNelson Czy możesz wyjaśnić, dlaczego HttpClient z Web API Client jest fantastyczny dla klienta REST JSON / XML?
user3092913,
2
Oto kilka słów na temat różnicy między HttpClient a WebClient: blogs.msdn.com/b/henrikn/archive/2012/02/11/…
JustAndrei

Odpowiedzi:

243

Mieszkam w światach F # i Web API.

Z interfejsem API sieci Web dzieje się wiele dobrych rzeczy, szczególnie w postaci programów obsługi komunikatów dla bezpieczeństwa itp.

Wiem, że moja jest tylko jedną opinią, ale poleciłabym ją tylko HttpClientdo przyszłych prac . Być może istnieje jakiś sposób na wykorzystanie niektórych innych elementów, które powstają System.Net.Httpbez bezpośredniego użycia tego zestawu, ale nie mogę sobie wyobrazić, jak to by działało w tym momencie.

Mówiąc o porównaniu tych dwóch

  • HttpClient jest bliżej HTTP niż WebClient.
  • HttpClient nie miał być całkowitym zamiennikiem klienta WWW, ponieważ istnieją takie rzeczy, jak postęp raportu, niestandardowy schemat URI i wykonywanie połączeń FTP, które zapewnia WebClient - ale HttpClient nie.
+--------------------------------------------+--------------------------------------------+
|               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                |
+--------------------------------------------+--------------------------------------------+

Jeśli używasz .NET 4.5, skorzystaj z asynchronicznej dobroci z HttpClient, którą Microsoft zapewnia programistom. HttpClient jest bardzo symetryczny dla braci HTTP po stronie serwera, są to HttpRequest i HttpResponse.

Aktualizacja: 5 powodów, dla których warto użyć nowego interfejsu API HttpClient:

  • Mocno wpisane nagłówki.
  • Udostępnione pamięci podręczne, pliki cookie i dane uwierzytelniające
  • Dostęp do plików cookie i udostępnionych plików cookie
  • Kontrola buforowania i współdzielonej pamięci podręcznej.
  • Wstaw moduł kodu do potoku ASP.NET. Czystszy i modułowy kod.

Odniesienie

C # 5.0 Joseph Albahari

(Channel9 - Video Build 2013)

Pięć wspaniałych powodów, aby używać nowego interfejsu API HttpClient do łączenia się z usługami sieci Web

WebClient vs HttpClient vs HttpWebRequest

Anant Dabhi
źródło
4
Należy wspomnieć, że HttpClient jest również dostępny dla .NET 4.0 .
Todd Menier,
2
To nie wyjaśnia, dlaczego WebClient wydaje się być o wielkości szybszy niż HttpClient. WebClientWydaje się również, że teraz ma metody asynchroniczne.
zmiażdżyć
8
@crush to dlatego, że OP tworzy nową instancję HttpClient dla każdego żądania. Zamiast tego należy użyć pojedynczego wystąpienia HttpClient przez cały okres użytkowania aplikacji. Zobacz stackoverflow.com/a/22561368/57369
Gabriel,
6
Warto zauważyć, że WebClientnie jest dostępny, .Net Coreale HttpClientjest.
Pranav Singh
3
Ponieważ .Net Core 2.0 WebClient (spośród tysięcy innych interfejsów API) powrócił i jest dostępny.
CoderBang
56

HttpClient jest nowszym interfejsem API i ma zalety

  • ma dobry model programowania asynchronicznego
  • nad którym pracuje Henrik F. Nielson, który jest w zasadzie jednym z wynalazców HTTP, i zaprojektował interfejs API, dzięki czemu można łatwo przestrzegać standardu HTTP, np. generować nagłówki zgodne ze standardami
  • znajduje się w .Net Framework 4.5, więc ma pewien gwarantowany poziom wsparcia dla dającej się przewidzieć przyszłości
  • posiada również wersję biblioteki xcopyable / portable-framework, jeśli chcesz go używać na innych platformach - .Net 4.0, Windows Phone itp.

Jeśli piszesz usługę internetową, która wykonuje wywołania REST do innych usług internetowych, powinieneś chcieć używać modelu programowania asynchronicznego dla wszystkich swoich wywołań REST, aby nie uderzyć w głód wątku. Prawdopodobnie chcesz również użyć najnowszego kompilatora C #, który obsługuje async / oczekuje.

Uwaga: AFAIK nie jest bardziej wydajny. Prawdopodobnie działa podobnie podobnie, jeśli stworzysz uczciwy test.

Tim Lovell-Smith
źródło
Gdyby miał sposób na zmianę proxy, byłby szalony
ed22
3

Po pierwsze, nie jestem autorytetem w stosunku do WebClient vs. HttpClient. Po drugie, z powyższych komentarzy wydaje się sugerować, że WebClient jest TYLKO synchronizowany, podczas gdy HttpClient to oba.

Zrobiłem szybki test wydajności, aby dowiedzieć się, jak działają WebClient (połączenia synchroniczne), HttpClient (synchronizacja i asynchronizacja). a oto wyniki.

Widzę to jako ogromną różnicę w myśleniu o przyszłości, tj. O długich procesach, responsywnym GUI itp. (Dodaj do korzyści, którą sugerujesz w frameworku 4.5 - który moim zdaniem jest znacznie szybszy w IIS)

Anthony Horne
źródło
4
WebClientwydaje się mieć funkcje asynchroniczne w najnowszych wersjach .NET. Chciałbym wiedzieć, dlaczego wydaje się być lepszym od HttpClient na tak ogromną skalę.
zmiażdżyć
1
Według stackoverflow.com/a/4988325/1662973 wydaje się, że jest taki sam, poza tym, że jeden jest abstrakcją drugiego. Być może zależy to od sposobu użycia / załadowania obiektów. Minimalny czas potwierdza stwierdzenie, że webclient jest w rzeczywistości abstrakcją HttpClient, więc narzut jest wart milisekundę. Struktura może być „podstępna” w tym, jak naprawdę łączy lub usuwa klienta WWW.
Anthony Horne
2

Mam test porównawczy między HttpClient, WebClient, HttpWebResponse, a następnie wywołaj Rest Web Api

i wynik Benchmark Call Rest Web Api

--------------------- Etap 1 ---- 10 Wniosek

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

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

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

--------------------- Etap 1 ---- 10 Wniosek - Mały rozmiar

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

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

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

--------------------- Etap 3 ---- Żądanie synchronizacji 10 - Mały rozmiar

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

{00: 00: 03.5505249} ====> WebRequest

{00: 00: 04.0761359} ====> WebClient

--------------------- Etap 4 ---- Żądanie synchronizacji 100 - Mały rozmiar

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

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

{00: 01: 01.2319499} ====> WebClient

--------------------- Etap 5 ---- 10 żądanie synchronizacji - maksymalny rozmiar

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

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

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

--------------------- Etap 6 ---- 10 żądanie synchronizacji - maksymalny rozmiar

{00: 01: 04.9964278} ====> HttpClinet

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

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

_____ WebClient jest szybszy ()

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;

//-------------------------Funkcje

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;
    }
użytkownik3754071
źródło
1
Zobacz komentarz Gabriela powyżej. Krótko mówiąc, HttpClient jest znacznie szybszy, jeśli utworzysz jedno wystąpienie HttpClient i użyjesz go ponownie.
LT Dan
1

Być może mógłbyś pomyśleć o problemie w inny sposób. WebClienti HttpClientsą zasadniczo różnymi implementacjami tej samej rzeczy. To, co zalecam, to wdrożenie wzorca Dependency Injection z kontenerem IoC w całej aplikacji. Powinieneś zbudować interfejs klienta z wyższym poziomem abstrakcji niż niskopoziomowy transfer HTTP. Możesz pisać konkretne klasy, które używają zarówno WebClientiHttpClient , a następnie użyć kontenera IoC wstrzyknąć realizację poprzez config.

Pozwoliłoby to na przełączanie się między nimi HttpClienti WebClientłatwość testowania w środowisku produkcyjnym.

Więc pytania takie jak:

Czy HttpClient będzie lepszym wyborem projektowym, jeśli uaktualnimy do .Net 4.5?

Można faktycznie odpowiedzieć obiektywnie, przełączając się między dwiema implementacjami klienta za pomocą kontenera IoC. Oto przykładowy interfejs, na którym możesz polegać, który nie zawiera żadnych szczegółów na temat HttpClientlub WebClient.

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

Pełny kod

Implementacja HttpClient

Możesz użyć, Task.Runaby WebClienturuchomić asynchronicznie w jego implementacji.

Wstrzykiwanie zależności, gdy jest dobrze wykonane, pomaga złagodzić problem związany z koniecznością podejmowania wcześniejszych decyzji na niskim poziomie. Ostatecznie jedynym sposobem na poznanie prawdziwej odpowiedzi jest wypróbowanie zarówno w środowisku na żywo, jak i sprawdzenie, który z nich działa najlepiej. Jest całkiem możliwe, że WebClientmoże działać lepiej dla niektórych klientów i HttpClientmoże działać lepiej dla innych. Dlatego abstrakcja jest ważna. Oznacza to, że kod można szybko zamienić lub zmienić w konfiguracji bez zmiany podstawowego projektu aplikacji.

Melbourne Developer
źródło
1

Niepopularna opinia z 2020 roku:

Kiedy przychodzi do ASP.NET apps I wciąż wolą WebClientsię HttpClient, ponieważ:

  1. Nowoczesna implementacja zawiera asynchroniczne / oczekiwane metody oparte na zadaniach
  2. Ma mniejszą pamięć i 2x-5x szybszy (inne odpowiedzi już o tym wspominają)
  3. Sugeruje się, aby „ ponownie użyć jednego wystąpienia HttpClient przez cały okres użytkowania aplikacji ”. Ale ASP.NET nie ma „czasu życia aplikacji”, tylko czas życia żądania.
Alex
źródło