Використання EnsureSuccessStatusCode та обробка викидів HttpRequestException


104

Яка схема використання HttpResponseMessage.EnsureSuccessStatusCode()? Він розпоряджається вмістом повідомлення і кидає HttpRequestException, але я не бачу, як програматично впоратися з цим інакше, ніж загальне Exception. Наприклад, сюди не входить те HttpStatusCode, що було б корисно.

Чи є спосіб отримати з нього більше інформації? Хтось може показати відповідний шаблон використання як EnsureSuccessStatusCode()HttpRequestException?

Відповіді:


156

Ідіоматичним використанням EnsureSuccessStatusCodeє стисла перевірка успішності запиту, коли ви не хочете обробляти випадки відмов якимось конкретним чином. Це особливо корисно, коли ви хочете швидко створити прототип клієнта.

Коли ви вирішили, що хочете розглядати випадки відмов певним чином, не робіть наступного.

var response = await client.GetAsync(...);
try
{
    response.EnsureSuccessStatusCode();
    // Handle success
}
catch (HttpRequestException)
{
    // Handle failure
}

Це викидає виняток лише для того, щоб негайно вловити його, що не має ніякого сенсу. Для IsSuccessStatusCodeцього HttpResponseMessageіснує властивість . Зробіть натомість наступне.

var response = await client.GetAsync(...);
if (response.IsSuccessStatusCode)
{
    // Handle success
}
else
{
    // Handle failure
}

1
Чи є спосіб отримати реальний цілочисельний код стану? коли я спробую це, я отримую рядок типу "NotFound" замість коду стану 404.
NickG

12
@NickG (int)response.StatusCode(Див. Msdn.microsoft.com/en-us/library/… )
Тимофій Шилдс

1
Зауважте, що за замовчуванням HttpRequestException, викинутий EnsureSuccessStatusCode (), буде мати фразу причин. Але ви все одно можете отримати доступ до цього властивості у відповіді, якщо воно не буде успішним.
Stefan Zvonar

@StefanZvonar Я не можу знайти фразу причини у винятку як те, що ви написали.
KansaiRobot

1
@NickG Ви можете використовувати (int) response.StatusCode для отримання числового значення коду статусу HTTP
Henrik Holmgaard Høyer

95

Мені не подобається EnsureSuccessStatusCode, оскільки він не повертає нічого значущого. Ось чому я створив власне розширення:

public static class HttpResponseMessageExtensions
{
    public static async Task EnsureSuccessStatusCodeAsync(this HttpResponseMessage response)
    {
        if (response.IsSuccessStatusCode)
        {
            return;
        }

        var content = await response.Content.ReadAsStringAsync();

        if (response.Content != null)
            response.Content.Dispose();

        throw new SimpleHttpResponseException(response.StatusCode, content);
    }
}

public class SimpleHttpResponseException : Exception
{
    public HttpStatusCode StatusCode { get; private set; }

    public SimpleHttpResponseException(HttpStatusCode statusCode, string content) : base(content)
    {
        StatusCode = statusCode;
    }
}

Вихідний код для EnsureSuccessStatusCode від Microsoft можна знайти тут

Синхронна версія на основі SO-посилання :

public static void EnsureSuccessStatusCode(this HttpResponseMessage response)
{
    if (response.IsSuccessStatusCode)
    {
        return;
    }

    var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();

    if (response.Content != null)
        response.Content.Dispose();

    throw new SimpleHttpResponseException(response.StatusCode, content);
}

Що мені не подобається в IsSuccessStatusCode, це те, що він не "добре" для багаторазового використання. Наприклад, ви можете використовувати бібліотеку типу polly, щоб повторити запит у разі виникнення проблем з мережею. У цьому випадку вам потрібен ваш код, щоб викликати виняток, щоб Polly або якась інша бібліотека могла з цим впоратися ...


1
погодьтеся, у коді за замовчуванням відсутня функція, щоб отримати значуще повідомлення з повернення.
LT

2
Ваша версія працює інакше, ніж оригінальна реалізація EnsureSuccessStatusCode. Ви завжди розпоряджаєтесь response.Content(оскільки нарешті викликається завжди навіть після return;заяви), і це знищує вміст для подальшого читання. Оригінальна реалізація містить вміст лише тоді, коли код статусу не вказує на успішний результат.
Лукаш.Навратіль

4
Я не розумію, чому ви спочатку, await response.Content.ReadAsStringAsync()а потім перевіряєтеif (response.Content != null)
мафу

3
Зараз Поллі обробляє результати повернення, а також винятки, саме для допомоги у подібному сценарії. Ви можете налаштувати Polly для захисту HttpRequestдзвінків і налаштувати політику як для обробки певних винятків, так і для певних HttpResponseCodes. Див. Приклад у читанні Поллі тут
гірський мандрівник

2
Як це може response.Contentбути нульовим, коли у нього тільки що був викликаний метод?
Ян Варбуртон

1

Я використовую EnsureSuccessStatusCode, коли не хочу обробляти винятки за тим самим методом.

public async Task DoSomethingAsync(User user)
{
    try
    {
        ...
        var userId = await GetUserIdAsync(user)
        ...
    }
    catch(Exception e)
    {
        throw;
    }
}

public async Task GetUserIdAsync(User user)
{
    using(var client = new HttpClient())
    {
        ...
        response = await client.PostAsync(_url, context);

        response.EnsureSuccesStatusCode();
        ...
    }
}

Виняток, переданий GetUserIdAsync, оброблятиметься у DoSomethingAsync.


0

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

Плюси

  • Журнал вмісту для помилки на сервері 5xx
    • Іноді помилка сервера насправді є замаскованою помилкою клієнта, наприклад, клієнт, що використовує застарілу кінцеву точку, яка нарешті вимкнена.
  • Полегшує виявлення помилок під час написання інтеграційних тестів з використанням ConfigureTestContainer<T>

Мінуси

  • Неефективна.
    • Якщо ви прочитаєте вміст відповідей, а вміст дуже довгий, ви сповільнить клієнта. Для деяких клієнтів із м'якими вимогами до реагування в реальному часі цей тремтіння може бути неприйнятним.
  • Неправильна відповідальність за реєстрацію та моніторинг помилок.
    • Якщо це помилка сервера 5xx, чому клієнт піклується, оскільки клієнт нічого не зробив? Просто зателефонуйте response.EnsureSuccessStatusCode();і дозвольте серверу з цим впоратися.
    • Чому б не просто перевірити журнали помилок сервера, коли є внутрішня помилка сервера?
  • Потрібно прочитати Contentресурс до перевірки стану. Можуть бути ситуації, коли це небажано, одна з яких - неефективність.

Використання

using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, "controller/action"))
{
  using (var response = await HttpClient.SendAsync(requestMessage))
  {
    var content = await response.Content.ReadAsStringAsync();
    response.EnsureSuccessStatusCode2(content);
    var result = JsonConvert.DeserializeObject<ResponseClass>(content);
  }
}

API

    public static class HttpResponseMessageExtensions
    {
        public static void EnsureSuccessStatusCode2(this HttpResponseMessage message, string content = null)
        {
            if (message.IsSuccessStatusCode)
                return;
            var contentMessage = string.IsNullOrWhiteSpace(content) ? string.Empty : $"Content: {content}";
            throw new HttpRequestException(string.Format(
                System.Globalization.CultureInfo.InvariantCulture,
                "Response status code does not indicate success: {0} ({1}).{2}",
                (int)message.StatusCode,
                message.ReasonPhrase,
                contentMessage)
                );
        }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.