Навіщо використовувати HttpClient для синхронного з'єднання


188

Я будую бібліотеку класів для взаємодії з API. Мені потрібно зателефонувати в API та обробити відповідь XML. Я бачу переваги використання HttpClientдля асинхронного підключення, але те, що я роблю, суто синхронне, тому я не бачу суттєвої користі від використання HttpWebRequest.

Якщо хтось може пролити будь-яке світло, я дуже вдячний. Я не один для того, щоб використовувати нову технологію заради неї.


3
Я ненавиджу вам сказати, але дзвінок через HTTP ніколи не є чисто синхронним через те, як внутрішня мережа Windows працює (він же і порти завершення).
TomTom


Відповіді:


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
Чи не може ваше синхронне використання методів асинхронності блокувати ваш потік інтерфейсу? Ви можете 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

Я б повторно повторив відповідь Донні В. та відповіді Джоша

"Єдина причина, по якій я не використовував би версію async, - це якщо я намагався підтримати старішу версію .NET, яка ще не має вбудованої підтримки async."

(і подати заяву, якщо я мав репутацію.)

Я не можу пригадати востаннє, якщо коли-небудь, я був вдячний факту, що HttpWebRequest викинув винятки для кодів статусу> = 400. Щоб обійти ці проблеми, потрібно негайно зловити винятки та віднести їх до деяких механізмів реагування на виключення у вашому коді ... нудно, нудно і схильність до помилок. Незалежно від того, чи це спілкування з базою даних, або реалізація веб-проксі-сервера на замовлення, майже "завжди" бажано, щоб драйвер Http просто повідомив вашому коду програми, що було повернуто, і вирішити, як вести себе.

Отже HttpClient є кращим.


1
Я був здивований, що HttpClientсама по собі є обгорткою HttpWebRequest(що справді, внутрішньо ловить ці WebExceptionоб’єкти і робить перетворення на a HttpResponseMessageдля вас). Я б подумав, що простіше побудувати нового клієнта повністю з нуля.
Дай

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

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

7

Якщо ви будуєте бібліотеку класів, можливо, користувачі вашої бібліотеки хотіли б використовувати вашу бібліотеку асинхронно. Я думаю, що це найбільша причина саме там.

Ви також не знаєте, як використовуватиметься ваша бібліотека. Можливо, користувачі будуть обробляти багато і багато запитів, і це робити асинхронно допоможе виконувати її швидше та ефективніше.

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

Єдина причина, по якій я не використовував би версію async - це якщо я намагався підтримати старішу версію .NET, яка ще не має вбудованої підтримки async.


Я бачу, тому зробіть бібліотеку класів асинхронною і дозвольте користувачам системи вирішити, чи використовувати її асинхронність, чи використовувати функцію очікування та використовувати її синхронно?
Кетчуп

erm, 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 Handler (.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.