Utilisation d'EnsureSuccessStatusCode et gestion de HttpRequestException qu'il lève

105

Quel est le modèle d'utilisation HttpResponseMessage.EnsureSuccessStatusCode()? Il supprime le contenu du message et le lance HttpRequestException, mais je ne vois pas comment le gérer par programme différemment d'un générique Exception. Par exemple, il n'inclut pas le HttpStatusCode, ce qui aurait été pratique.

Existe-t-il un moyen d'en tirer plus d'informations? Quelqu'un pourrait-il montrer le modèle d'utilisation pertinent des deux EnsureSuccessStatusCode()et HttpRequestException?

G. Stoynev
la source

Réponses:

157

L'utilisation idiomatique de EnsureSuccessStatusCodeconsiste à vérifier de manière concise le succès d'une requête, lorsque vous ne souhaitez pas gérer les cas d'échec d'une manière spécifique. Ceci est particulièrement utile lorsque vous souhaitez prototyper rapidement un client.

Lorsque vous décidez de gérer les cas d'échec d'une manière spécifique, ne procédez pas comme suit.

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

Cela lève une exception juste pour l'attraper immédiatement, ce qui n'a aucun sens. La IsSuccessStatusCodepropriété de HttpResponseMessageest là à cet effet. Faites plutôt ce qui suit.

var response = await client.GetAsync(...);
if (response.IsSuccessStatusCode)
{
    // Handle success
}
else
{
    // Handle failure
}
Timothy Shields
la source
1
Existe-t-il un moyen d'obtenir le vrai code d'état entier ? quand j'essaye ceci j'obtiens une chaîne telle que "NotFound" au lieu du code d'état 404.
NickG
12
@NickG (int)response.StatusCode(Voir msdn.microsoft.com/en-us/library/… )
Timothy Shields
1
Notez que l'exception HttpRequestException par défaut lancée par EnsureSuccessStatusCode () aura la phrase de raison. Mais vous pouvez quand même accéder à cette propriété dans la réponse si elle échoue.
Stefan Zvonar
@StefanZvonar Je ne trouve pas la phrase de raison dans l'exception comme ce que vous avez écrit.
KansaiRobot
1
@NickG Vous pouvez utiliser (int) response.StatusCode pour obtenir la valeur numérique du code d'état HTTP
Henrik Holmgaard Høyer
95

Je n'aime pas EnsureSuccessStatusCode car il ne renvoie rien de signifiant. C'est pourquoi j'ai créé ma propre extension:

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;
    }
}

le code source d'EnsureSuccessStatusCode de Microsoft peut être trouvé ici

Version synchrone basée sur le lien 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);
}

Ce que je n'aime pas avec IsSuccessStatusCode, c'est qu'il n'est pas "joliment" réutilisable. Par exemple, vous pouvez utiliser une bibliothèque comme polly pour répéter une requête en cas de problème de réseau. Dans ce cas, vous avez besoin que votre code lève une exception afin que polly ou une autre bibliothèque puisse la gérer ...

pajics
la source
1
d'accord, le code par défaut manque la fonctionnalité pour obtenir un message significatif du retour.
LT
2
Votre version fonctionne différemment de l'implémentation d'origine de EnsureSuccessStatusCode. Vous disposez toujours du response.Content(car finalement est appelé toujours même après l' return;instruction) et il détruit le contenu pour une lecture ultérieure. L'implémentation d'origine supprime le contenu uniquement lorsque le code d'état n'indique pas de résultat réussi.
Lukas.Navratil
4
Je ne comprends pas pourquoi vous d'abord await response.Content.ReadAsStringAsync(), puis vérifiezif (response.Content != null)
mafu
3
Polly gère désormais les résultats de retour ainsi que les exceptions, précisément pour faciliter ce type de scénario. Vous pouvez configurer Polly pour protéger les HttpRequestappels et configurer la stratégie à la fois pour gérer certaines exceptions et certaines HttpResponseCodes. Voir l' exemple dans le readme de Polly ici
mountain traveller
2
Comment pourrait-il response.Contentêtre nul alors qu'il vient de recevoir une méthode appelée?
Ian Warburton
1

J'utilise EnsureSuccessStatusCode lorsque je ne veux pas gérer l'exception sur la même méthode.

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();
        ...
    }
}

L'exception levée sur GetUserIdAsync sera gérée sur DoSomethingAsync.

Sérgio Damasceno
la source
0

Voici ma solution proposée. Le seul défaut est que, puisque le gestionnaire de ressources du framework ASP.NET Core est interne au framework, je ne peux pas réutiliser directement les chaînes de message internationalisées de Microsoft, donc j'utilise simplement le littéral de message anglais textuel ici.

Avantages

  • Enregistre le contenu pour une erreur de serveur 5xx
    • Parfois, une erreur de serveur est en fait une erreur de client déguisée, comme un client utilisant un point de terminaison obsolète qui a finalement été arrêté.
  • Facilite la détection des erreurs lors de l'écriture des tests d'intégration à l'aide de ConfigureTestContainer<T>

Les inconvénients

  • Inefficace.
    • Si vous lisez le contenu de la réponse et que le contenu est très long, vous ralentirez le client. Pour certains clients, avec des exigences de réponse douce en temps réel, cette gigue peut être inacceptable.
  • Responsabilité incorrecte pour la journalisation et la surveillance des erreurs.
    • S'il s'agit d'une erreur de serveur 5xx, pourquoi le client s'en soucie-t-il, puisque le client n'a rien fait de mal? Appelez simplement response.EnsureSuccessStatusCode();et laissez le serveur s'en occuper.
    • Pourquoi ne pas simplement vérifier les journaux d'erreurs du serveur en cas d'erreur interne du serveur?
  • Nécessite la lecture de la Contentpropriété avant de vérifier l'état. Il peut y avoir des situations où cela n'est pas souhaitable, dont l'inefficacité.

Usage

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)
                );
        }
    }
John Zabroski
la source