Как я могу узнать, когда истекло время ожидания HttpClient?

146

Насколько я могу судить, нет никакого способа узнать, что это именно тайм-аут. Я ищу не в том месте или мне не хватает чего-то большего?

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 01
Огромное количество голосов за вопрос. Также ... есть идеи, как это сделать на UWP? Его Windows.Web.HTTP.HTTPClient не имеет элемента тайм-аута. Также метод GetAsync не принимает токен отмены ...
Do-do-new
1
6 лет спустя, и все еще кажется невозможным узнать, истек ли тайм-аут клиента.
Стив Смит

Ответы:

63

Вам нужно дождаться 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);
}
Murkaeus
источник
2
Я проверил это и GetStreamAsyncбросил TaskCanceledExceptionмне.
Сэм
39
Как узнать, TaskCanceledExceptionвызвано ли это тайм-аутом HTTP, а не прямой отменой или другой причиной?
UserControl
9
@UserControl проверка TaskCanceledException.CancellationToken.IsCancellationRequested. Если false, вы можете быть уверены, что это был тайм-аут.
Todd Menier
4
Оказывается, вы не можете рассчитывать на IsCancellationRequestedполучение токена исключения при прямой отмене, как я думал ранее: stackoverflow.com/q/29319086/62600
Todd Menier
2
@testing Они не ведут себя иначе. Просто у вас есть один токен, который будет представлять запрос на отмену пользователя, и внутренний (вы не можете получить доступ и вам не нужен), который представляет тайм-аут клиента. Отличается именно случай использования
сэр Руфо,
61

Я воспроизвожу ту же проблему, и это очень раздражает. Я нашел это полезным:

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 (Exception e) {} Перехватывается только общее исключение, а не TaskCanceledException. Что не так в моей версии кода?
Наоми
1
Я создал новый отчет об ошибке , так как оригинал один , как представляется, в архивном форуме сообщение: connect.microsoft.com/VisualStudio/feedback/details/3141135
StriplingWarrior
1
Если токен передается извне, проверьте его не default(CancellationToken)до сравнения с ex.CancellationToken.
SerG
27

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

Затем вы получаете доступ к Statusсвойству, см. WebExceptionStatus

user247702
источник
3
Хм, я возвращаюсь AggregateExceptionс TaskCancelledExceptionвнутренней стороны. Я, должно быть, делаю что-то не так ...
Бенджол
Вы используете catch(WebException e)?
user247702
Нет, и если я попробую, то AggregateExceptionэто останется без внимания. Если вы создаете проект консоли VS, добавляете ссылку System.Net.Httpи перетаскиваете код main, вы можете убедиться в этом сами (если хотите).
Benjol
5
Если период ожидания превышает период ожидания задачи, вы получите файл TaskCanceledException. Кажется, это вызвано внутренней обработкой тайм-аута TPL на более высоком уровне, чем HttpWebClient. Там , кажется, не быть хорошим способом отличить отмены тайм - аута и отмены пользователем. В результате вы можете не получить WebExceptionв своем AggregateException.
JT.
1
Как говорили другие, вы должны предположить, что TaskCanceledException было тайм-аутом. Я использую try {// Код здесь} catch (исключение AggregateException) {if (exception.InnerExceptions.OfType <TaskCanceledException> () .Any ()) {// Обрабатываем тайм-аут здесь}}
Vdex,
8

По сути, вам нужно поймать OperationCanceledExceptionи проверить состояние токена отмены, который был передан SendAsync(или GetAsync, или любой другой HttpClientметод, который вы используете):

  • если он был отменен ( IsCancellationRequestedправда), значит, запрос действительно был отменен
  • если нет, это означает, что запрос истек

Конечно, это не очень удобно ... лучше бы получить TimeoutExceptionв случае таймаута. Я предлагаю здесь решение, основанное на настраиваемом обработчике HTTP-сообщений: Лучшая обработка тайм-аута с помощью HttpClient

Томас Левеск
источник
ах! это ты! Сегодня я написал комментарий в вашем блоге. Но что касается этого ответа, я думаю, что ваша точка зрения об IsCancellationRequested неверна, потому что, похоже, это всегда верно для меня, когда я не отменял его сам
knocte
@knocte, это странно ... Но в этом случае решение из моего сообщения в блоге вам не поможет, поскольку оно также полагается на это
Томас Левеск
1
в вопросе github по этому поводу многие утверждают, что я сказал: IsCancellationRequested истинно, когда есть тайм-аут; так что я хочу снизить ваш ответ;)
knocte
@knocte, я не знаю, что тебе сказать ... Я давно этим пользуюсь и у меня всегда работало. Вы установили HttpClient.Timeoutбесконечность?
Thomas Levesque
нет, я этого не делал, потому что сам не могу контролировать HttpClient, это сторонняя библиотека, которую я использую, та, которая ее использует
knocte
-2
_httpClient = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(5)};

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

Syv Development
источник
3
Вот как вы устанавливаете тайм-аут httpclient. Это не решает вопрос о том, как узнать, что время httpclient истекло.
Итан Фишер,