Зачем использовать HttpClient для синхронного подключения


189

Я строю библиотеку классов для взаимодействия с API. Мне нужно вызвать API и обработать ответ XML. Я вижу преимущества использования HttpClientасинхронного подключения, но то, что я делаю, чисто синхронно, поэтому я не вижу каких-либо существенных преимуществ по сравнению с использованием HttpWebRequest.

Если кто-то может пролить свет, я был бы очень признателен. Я не из тех, кто использует новые технологии ради этого.


3
Ненавижу вам говорить, но вызов по HTTP никогда не бывает чисто синхронным из-за того, как внутренне работает Windows-сеть (или порты завершения).
TomTom


Кроме того, это полезно знать - эффективно использовать async / await с 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);
    }
}

Что касается того, почему вы должны использовать HttpClientover WebRequest, то HttpClientэто новый ребенок в блоке, который может содержать улучшения по сравнению со старым клиентом.


27
Не будет ли ваше синхронное использование асинхронных методов потенциально блокировать ваш поток пользовательского интерфейса? Вы можете рассмотреть что - то вроде string responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;вместо этого , если вы должны сделать это синхронным.
землянин

13
@earthling, да, Task.Runвызывает задачу из ThreadPool, но вы вызываете .Resultее, убивая все выгоды от этого и блокируя поток, в котором вы вызвали это .Result(который обычно бывает основным потоком пользовательского интерфейса).
Дарин Димитров

35
Согласно этому сообщению ( blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx ) .Resultподобные вызовы могут исчерпать пул потоков и вызвать взаимоблокировку.
Пит Гарафано

16
Этот код всегда блокируется, если выполняется в задаче, созданной в основном потоке пользовательского интерфейсаnew TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()
Wim Coenen

24
Итак, как мне использовать HttpClient синхронно из потока пользовательского интерфейса? Допустим, я намеренно хочу заблокировать поток пользовательского интерфейса (или пишу консольное приложение), пока не получу ответ HTTP ... Так что, если Wait (), Result () и т. Д. Могут вызвать взаимоблокировки, какое определенное решение для этого без риск тупика и без использования других классов, таких как WebClient?
Декстер

26

Я бы повторил ответ Донни В. и Джоша

«Единственная причина, по которой я бы не использовал асинхронную версию, заключается в том, что я пытался поддерживать более старую версию .NET, которая еще не имеет встроенной поддержки асинхронности».

(и upvote, если у меня была репутация.)

Я не могу вспомнить последний раз, если когда-либо, я был благодарен за тот факт, что HttpWebRequest выдал исключения для кодов состояния> = 400. Чтобы обойти эти проблемы, вам нужно немедленно перехватить исключения и сопоставить их с некоторыми механизмами реагирования на исключения. в вашем коде ... скучно, утомительно и подвержено ошибкам само по себе. Будь то связь с базой данных или реализация сделанного на заказ веб-прокси, «почти» всегда желательно, чтобы драйвер Http просто сообщал вашему коду приложения, что было возвращено, и оставлял вам решать, как себя вести.

Следовательно, HttpClient предпочтительнее.


1
Я был удивлен, что HttpClientсам по себе является оберткой HttpWebRequest(которая действительно внутренне ловит эти WebExceptionобъекты и выполняет преобразование в a HttpResponseMessageдля вас). Я бы подумал, что будет проще построить новый клиент с нуля.
Дай

4
Существует множество веских причин, например, нежелание переписывать всю кодовую базу только для http-вызова очень низкого уровня, который даже не критичен по производительности (но привел бы к асинхронности для миллиона мест).
FrankyBoy

В .net core 2, если вы хотите динамически оценивать выражение с помощью DynamicExpressionParser, может быть невозможно использовать async; индексаторы свойств не могут использовать async; в моей ситуации мне нужно динамически оценивать строку типа "GetDefaultWelcomeMessage [\" InitialMessage \ "]", где этот метод создает HttpCall, а синтаксис индекса предпочтительнее синтаксиса метода "Util.GetDefaultWelcomeMessage (\" InitialMessage \ ")"
eugen

7

Если вы создаете библиотеку классов, то, возможно, пользователи вашей библиотеки захотят использовать вашу библиотеку асинхронно. Я думаю, что это самая главная причина прямо здесь.

Вы также не знаете, как будет использоваться ваша библиотека. Возможно, пользователи будут обрабатывать много-много запросов, и асинхронное выполнение этого поможет быстрее и эффективнее.

Если вы можете сделать это просто, постарайтесь не возлагать бремя на пользователей вашей библиотеки, пытаясь сделать поток асинхронным, когда вы можете позаботиться об этом за них.

Единственная причина, по которой я бы не использовал асинхронную версию, заключается в том, что я пытался поддерживать более старую версию .NET, которая еще не имеет встроенной поддержки асинхронности.


Я вижу, поэтому сделайте библиотеку классов асинхронной и разрешите пользователям системы решать, использовать ли ее асинхронно или использовать await и использовать ее синхронно?
Кетчуп

эм, ожидание помогает сделать определенные вызовы асинхронными, возвращая управление вызывающей стороне.
Джош Смитон

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
Thx @ Darkonekt ... Это прекрасно работает для меня. Только HttpClient.SendAsync (...). Результат никогда не работает внутри обработчика AspNet (.ASHX).
Рафаэль Казуо Сато Симао

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

если вы используете этот класс, передайте свой асинхронный метод в качестве параметра, вы можете безопасно вызывать асинхронные методы из методов синхронизации.

это объясняется здесь: https://cpratt.co/async-tips-tricks/


-1

Все ответы, кажется, сосредоточены на использовании HttpClientсинхронно, вместо того, чтобы давать фактический ответ на вопрос.

HttpClientэто больше, чем простой обработчик запросов / ответов, поэтому он может иметь дело с некоторыми особенностями различных сетей. А именно, в моем случае, работа с прокси-сервером NTLM, который требует согласования, отправки нескольких запросов / ответов с токенами и учетными данными между клиентом и прокси-сервером для аутентификации. HttpClient(Использование HttpClientHandler), кажется, имеет встроенный механизм, который обрабатывает, который возвращает ресурсы за прокси с помощью одного вызова метода.


Ваш ответ также не объясняет, как использовать HttpClient асинхронно.
user275801

@ user275801 Это глупый комментарий. Никто не спрашивал это. Это асинхронный по умолчанию.
Bizniztime
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.