Зачем использовать 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 предпочтительнее.

Trev
источник
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), кажется, имеет встроенный механизм, который обрабатывает, который возвращает ресурсы за прокси с помощью одного вызова метода.

Bizniztime
источник
Ваш ответ также не объясняет, как использовать HttpClient асинхронно.
user275801
@ user275801 Это глупый комментарий. Никто не спрашивал это. Это асинхронный по умолчанию.
Bizniztime