Як я можу дізнатися, коли час закінчився з часом HttpClient?


142

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

string baseAddress = "http://localhost:8080/";
var client = new HttpClient() 
{ 
    BaseAddress = new Uri(baseAddress), 
    Timeout = TimeSpan.FromMilliseconds(1) 
};
try
{
    var s = client.GetAsync("").Result;
}
catch(Exception e)
{
    Console.WriteLine(e.Message);
    Console.WriteLine(e.InnerException.Message);
}

Це повертає:

Виникла одна чи більше помилок.

Завдання скасовано.


3
Ми можемо підтримати цю проблему на GitHub: HttpClient кидає TaskCanceledException під час таймауту # 20296
csrowell

Величезний заклик до питання. Також ... будь-яка ідея, як це зробити на UWP? Його Windows.Web.HTTP.HTTPClient не має члена тайм-аута. Також метод GetAsync не приймає маркер скасування ...
Do-do-new

1
Через 6 років, і все ще не представляється можливим дізнатись, чи закінчився час клієнта.
Стів Сміт

Відповіді:


61

Вам потрібно дочекатися GetAsyncметоду. Потім він кине а, TaskCanceledExceptionякщо він закінчився. Додатково GetStringAsyncта GetStreamAsyncвнутрішньо обробляйте тайм-аут, тому вони НІКОЛИ не кидатимуть.

string baseAddress = "http://localhost:8080/";
var client = new HttpClient() 
{ 
    BaseAddress = new Uri(baseAddress), 
    Timeout = TimeSpan.FromMilliseconds(1) 
};
try
{
    var s = await client.GetAsync();
}
catch(Exception e)
{
    Console.WriteLine(e.Message);
    Console.WriteLine(e.InnerException.Message);
}

2
Я перевірив це і GetStreamAsyncкинув TaskCanceledExceptionна мене.
Сем

38
Як я можу визначити, чи TaskCanceledExceptionвикликано тимчасовим очікуванням HTTP, а ні, сказати пряме скасування чи іншу причину?
UserControl

8
@UserControl чек TaskCanceledException.CancellationToken.IsCancellationRequested. Якщо помилково, ви можете бути впевнені, що це був тайм-аут.
Тодд Меньє

3
Як виявляється, ви не можете розраховувати на IsCancellationRequestedвстановлення знака винятку щодо прямого скасування, як я раніше думав: stackoverflow.com/q/29319086/62600
Todd Menier

2
@ testing Вони не ведуть себе по- різному. Просто у вас є один маркер, який буде представляти запит на скасування користувача, і внутрішній (ви не можете отримати доступ і вам не потрібен), який представляє клієнтський час очікування. Саме випадок використання відрізняється
сер Руфо,

59

Я відтворюю те саме питання, і це насправді дратує. Я вважаю ці корисні:

HttpClient - робота з сукупними винятками

Помилка в HttpClient.GetAsync повинна кидати WebException, а не TaskCanceledException

Деякий код на випадок, якщо посилання нікуди не надходять:

var c = new HttpClient();
c.Timeout = TimeSpan.FromMilliseconds(10);
var cts = new CancellationTokenSource();
try
{
    var x = await c.GetAsync("http://linqpad.net", cts.Token);  
}
catch(WebException ex)
{
    // handle web exception
}
catch(TaskCanceledException ex)
{
    if(ex.CancellationToken == cts.Token)
    {
        // a real cancellation, triggered by the caller
    }
    else
    {
        // a web request timeout (possibly other things!?)
    }
}

На мій досвід, WebException не може бути спійманий ні за яких обставин. Чи переживають інші щось інше?
розчавити

1
@crush WebException можна зловити. Можливо, це допоможе.
DavidRR

Це не працює для мене, якщо я не використовую cts. Я просто використовую Task <T> task = SomeTask () try {T result = task.Result} catch (TaskCanceledException) {} catch (Виняток e) {} Зловлюється лише загальний виняток, а не TaskCanceledException. Що не так у моїй версії коду?
Наомі

1
Я створив новий звіт про помилку, оскільки оригінальний опис міститься в архіві форуму: connect.microsoft.com/VisualStudio/feedback/details/3141135
StriplingWarrior

1
Якщо маркер передається ззовні, перевіряйте його не default(CancellationToken)перед порівнянням ex.CancellationToken.
SerG

25

Я виявив, що найкращий спосіб визначити, чи закінчився виклик служби - використовувати маркер скасування, а не властивість тайм-аута HttpClient:

var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);

А потім обробіть функцію CancellationException під час службового дзвінка ...

catch(TaskCanceledException)
{
    if(!cts.Token.IsCancellationRequested)
    {
        // Timed Out
    }
    else
    {
        // Cancelled for some other reason
    }
}

Звичайно, якщо час очікування відбувається на стороні обслуговування речей, це повинно бути в змозі оброблятися WebException.


1
Хм, я гадаю, що оператор заперечення (який було додано в редакції) для цього зразка має бути видалений, щоб мати сенс? Якщо cts.Token.IsCancellationRequestedце trueмає означати , що тайм - аут стався?
Лассе Крістіансен

9

Від http://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout.aspx

Запит на систему доменних імен (DNS) може зайняти до 15 секунд, щоб повернутися або вимкнути час. Якщо ваш запит містить ім'я хоста, яке потребує розв'язання, і ви встановите для значення Timeout значення менше 15 секунд, перш ніж запустити WebException , може знадобитися 15 секунд або більше, щоб вказати час очікування на ваш запит .

Потім ви отримаєте доступ до Statusресурсу, див. WebExceptionStatus


3
Хм, я отримую назад AggregateExceptionз TaskCancelledExceptionвнутрішньої сторони . Я повинен робити щось не так ...
Бенжол

Ви використовуєте catch(WebException e)?
user247702

Ні, і якщо я спробую, то AggregateExceptionце без обробки. Якщо ви створюєте проект консолі VS, додаєте посилання System.Net.Httpта вкажіть код main, ви можете переконатися в цьому (якщо хочете).
Бенжол

5
Якщо період очікування перевищує період очікування завдання, ви отримаєте TaskCanceledException. Це, здається, кинуто внутрішньою тайм-аут обробкою TPL на більш високому рівні, ніж HttpWebClient. Там , здається, не бути хорошим способом відрізнити скасування тайм - ауту і скасування користувачем. Підсумок цього полягає в тому, що ви можете не отримати в WebExceptionмежах свого AggregateException.
JT.

1
Як говорили інші, ви повинні припустити, що TaskCanceledException був тайм-аутом. Я використовую спробу {// Code here} catch (виняток AggregateException) {if (izjem.InnerExceptions.OfType <TaskCanceledException> () .Any ()) {// Handle timeout here}}
Vdex

8

По суті, вам потрібно вловити OperationCanceledExceptionта перевірити стан токена скасування, який було передано SendAsync( GetAsyncабо будь-який HttpClientметод, який ви використовуєте):

  • якщо він був скасований ( IsCancellationRequestedвірно), це означає, що запит справді було скасовано
  • якщо ні, це означає, що запит вичерпано

Звичайно, це не дуже зручно ... краще було б отримати TimeoutExceptionу випадку тайм-ауту. Я пропоную тут рішення, що базується на користувальницькій обробці повідомлень HTTP: Краще обробка тайм-ауту з HttpClient


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

@knocte це дивно ... Але в такому випадку рішення з мого допису в блозі вам не допоможе, оскільки воно також покладається на це
Thomas Levesque

1
у випуску github з цього приводу багато хто стверджує, що я сказав: що IsCancellationRequested істинно, коли є час очікування; тож я спокусився спростувати вашу відповідь;)
knocte

@knocte, я не знаю, що тобі сказати ... Я цим користувався давно, і це завжди працювало на мене. Ви встановили HttpClient.Timeoutнескінченність?
Томас Левеск

ні, я цього не зробив, тому що я сам не можу керувати HttpClient, це бібліотека третьої сторони, яку я використовую
knocte

-1
_httpClient = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(5)};

це те, що я зазвичай роблю, здається, працює для мене досить добре, особливо добре, коли використовую проксі.


1
Таким чином ви встановлюєте час очікування httpclient. Це не стосується питання, що було сказати, як час httpclient закінчився.
Етан Фішер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.