WedX - журнал о программировании и компьютерных науках

HttpClient в инструкции using вызывает отмену задачи

Я создал FileResult : IHttpActionResult возвращаемый тип webapi для своих вызовов API. FileResult загружает файл с другого URL-адреса, а затем возвращает поток клиенту.

Изначально в моем коде была инструкция using, как показано ниже:

public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
    try
    {
        HttpResponseMessage response;
        using (var httpClient = new HttpClient())
        {

            response = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new System.Net.Http.StreamContent(
                                    await httpClient.GetStreamAsync(this.filePath))
            };
        }
        return response;
    }
    catch (WebException exception)
    {...}
}

Однако это периодически приводило к TaskCanceledException. Я знаю, что если HttpClient удаляется до завершения асинхронного вызова, состояние задачи изменится на отмененное. Однако, поскольку я использую ожидание в: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)), которое должно предотвратить удаление HttpClient в середине завершения задачи.

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

Когда я удалил оператор using, код работал правильно:

public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
    try
    {
        HttpResponseMessage response;
        var httpClient = new HttpClient();

        response = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new System.Net.Http.StreamContent(
                                await httpClient.GetStreamAsync(this.filePath))
        };
        return response;
    }
    catch (WebException exception)
    {...}
}

Есть идеи, почему использование вызвало проблему?


  • Вы пробовали использовать отладчик? Проверяем пошагово. 13.10.2015
  • Да. На самом деле это не помогает, поскольку исключение создается не здесь, а в совершенно другом месте. Это происходит в конвейере Owin, который я использую, где проходит аутентификация, и ожидает следующего запроса. 13.10.2015
  • Есть ли у TaskCanceledException какое-либо внутреннее исключение? 13.10.2015
  • Нет никакого внутреннего исключения. 13.10.2015

Ответы:


1

Я знаю, что если HttpClient удаляется до завершения асинхронного вызова, состояние задачи изменится на отмененное. Однако, поскольку я использую ожидание в: Content = new System.Net.Http.StreamContent (await httpClient.GetStreamAsync (this.filePath)), это должно предотвратить удаление HttpClient в середине завершения задачи.

Но что делает эта задача ? Она получает поток. Итак, ваш код заканчивается Stream, который может быть или не может быть полностью прочитан, когда он закрывает HttpClient.

HttpClient специально разработан для повторного использования (и одновременного использования), поэтому я рекомендую полностью удалить using и переместить объявление HttpClient в член класса static. Но если вы хотите закрыть и снова открыть клиенты, вы сможете заставить его работать, прочитав поток полностью в память перед закрытием HttpClient.

13.10.2015

2

У меня была аналогичная проблема с исключениями отмененной задачи. Если вы попытаетесь поймать AggregateException или установить блок catch all Exception под вашим WebException, вы вполне можете обнаружить, что поймали его, за одним исключением с записью «Задача была отменена»

Я провел небольшое расследование и обнаружил, что AggregateException вводит в заблуждение, как описано в различных ветках;

Установка HttpClient на слишком короткий тайм-аут приводит к сбою процесса

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

https://social.msdn.microsoft.com/Forums/en-US/d8d87789-0ac9-4294-84a0-91c9fa27e353/bug-in-httpclientgetasync-should-throw-webexception-not-taskcanceledexception?forum=netfxnetcom&prof=required

Я закончил тем, что изменил свой код, чтобы установить явный тайм-аут (где asyncTimeoutInMins читается из файла app.config);

        string jsonResponse = string.Empty;
        try
        {
            using (HttpClient httpClient = new HttpClient())
            {
                httpClient.BaseAddress = new Uri(Properties.Settings.Default.MyWebService);
                httpClient.DefaultRequestHeaders.Accept.Clear();
                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                httpClient.Timeout = new TimeSpan(0, asyncTimeoutInMins, 0);

                HttpResponseMessage response;

                response = await httpClient.GetAsync("/myservice/resource");

                // Check the response StatusCode
                if (response.IsSuccessStatusCode)
                {
                    // Read the content of the response into a string
                    jsonResponse = await response.Content.ReadAsStringAsync();
                }
                else if (response.StatusCode == HttpStatusCode.Forbidden)
                {
                    jsonResponse = await response.Content.ReadAsStringAsync();

                    Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse)));

                    Environment.Exit((int)ExitCodes.Unauthorised);
                }
                else
                {
                    jsonResponse = await response.Content.ReadAsStringAsync();

                    Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse)));

                    Environment.Exit((int)ExitCodes.ApplicationError);
                }
           }

        }
        catch (HttpRequestException reqEx)
        {
            Logger.Instance.Error(reqEx);

            Console.WriteLine("HttpRequestException : {0}", reqEx.InnerException.Message);

            Environment.Exit((int)ExitCodes.ApplicationError);
        }
        catch (Exception ex)
        {
            Logger.Instance.Error(ex);

            throw;
        }

        return jsonResponse;
13.10.2015
  • Как упоминалось в комментарии выше, исключение перехватывается не там, а на уровне логики аутентификации. 13.10.2015
  • Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]