Utilizzo di GuaranteSuccessStatusCode e gestione di HttpRequestException che genera


104

Qual è il modello di utilizzo di HttpResponseMessage.EnsureSuccessStatusCode()? Elimina il contenuto del messaggio e lo lancia HttpRequestException, ma non riesco a vedere come gestirlo a livello di codice in modo diverso da un generico Exception. Ad esempio, non include il HttpStatusCode, che sarebbe stato utile.

C'è un modo per ottenere più informazioni da esso? Qualcuno potrebbe mostrare un modello di utilizzo pertinente di entrambi EnsureSuccessStatusCode()e HttpRequestException?

Risposte:


156

L'utilizzo idiomatico di EnsureSuccessStatusCodeè quello di verificare in modo conciso il successo di una richiesta, quando non si desidera gestire i casi di errore in un modo specifico. Ciò è particolarmente utile quando si desidera prototipare rapidamente un client.

Quando decidi di gestire i casi di errore in un modo specifico, non eseguire le seguenti operazioni.

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

Questo genera un'eccezione solo per prenderlo immediatamente, il che non ha alcun senso. La IsSuccessStatusCodeproprietà di HttpResponseMessageè lì per questo scopo. Procedi invece come segue.

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

1
C'è un modo per ottenere il codice di stato intero reale ? quando provo questo ottengo una stringa come "NotFound" invece del codice di stato 404.
NickG

12
@NickG (int)response.StatusCode(vedere msdn.microsoft.com/en-us/library/… )
Timothy Shields

1
Nota, la HttpRequestException predefinita generata da GuaranteSuccessStatusCode () avrà la frase del motivo. Ma potresti comunque accedere a quella proprietà nella risposta se non ha successo.
Stefan Zvonar

@StefanZvonar Non riesco a trovare la frase del motivo nell'eccezione di quello che hai scritto.
KansaiRobot

1
@NickG Puoi usare (int) response.StatusCode per ottenere il valore numerico per il codice di stato HTTP
Henrik Holmgaard Høyer

95

Non mi piace GuaranteSuccessStatusCode in quanto non restituisce nulla di significativo. Ecco perché ho creato la mia estensione:

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

il codice sorgente per GuaranteSuccessStatusCode di Microsoft può essere trovato qui

Versione sincrona basata su SO link :

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

Quello che non mi piace di IsSuccessStatusCode è che non è "piacevolmente" riutilizzabile. Ad esempio è possibile utilizzare una libreria come polly per ripetere una richiesta in caso di problemi di rete. In quel caso hai bisogno del tuo codice per sollevare un'eccezione in modo che polly o qualche altra libreria possa gestirlo ...


1
d'accordo, nel codice predefinito manca la funzione per ottenere un messaggio significativo dal ritorno.
LT

2
La tua versione funziona in modo diverso dall'implementazione originale di EnsureSuccessStatusCode. Disponi sempre il response.Content(perché alla fine viene chiamato sempre anche dopo l' return;affermazione) e ne distrugge il contenuto per un'ulteriore lettura. L'implementazione originale elimina il contenuto solo quando il codice di stato non indica un risultato positivo.
Lukas.Navratil

4
Non capisco perché prima tu await response.Content.ReadAsStringAsync()e poi controlliif (response.Content != null)
mafu

3
Polly ora gestisce i risultati di ritorno così come le eccezioni, proprio per aiutare con questo tipo di scenario. È possibile configurare Polly per proteggere le HttpRequestchiamate e configurare il criterio per gestire determinate eccezioni e determinate HttpResponseCode. Vedi l' esempio nel file readme di Polly qui
viaggiatore di montagna

2
Come potrebbe response.Contentessere nullo quando è stato appena chiamato un metodo?
Ian Warburton

1

Uso GuaranteSuccessStatusCode quando non voglio gestire l'eccezione sullo stesso metodo.

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'eccezione generata su GetUserIdAsync verrà gestita su DoSomethingAsync.


0

Di seguito è la mia soluzione proposta. L'unico difetto è che poiché il gestore delle risorse del framework ASP.NET Core è interno al framework, non posso riutilizzare direttamente le stringhe di messaggi internazionalizzate di Microsoft, quindi qui sto usando solo il letterale del messaggio inglese letterale.

Professionisti

  • Registra il contenuto per un errore del server 5xx
    • A volte, un errore del server è in realtà un errore del client mascherato, come un client che utilizza un endpoint deprecato che alla fine è stato spento.
  • Rende più facile scoprire gli errori durante la scrittura di test di integrazione utilizzando ConfigureTestContainer<T>

Contro

  • Inefficiente.
    • Se leggi il contenuto della risposta e il contenuto è molto lungo, rallenterai il client. Per alcuni client, con requisiti di risposta soft in tempo reale, questo jitter potrebbe essere inaccettabile.
  • Responsabilità errata per la registrazione e il monitoraggio degli errori.
    • Se questo è un errore del server 5xx, perché il cliente si preoccupa, dal momento che il client non ha fatto nulla di sbagliato? Chiama response.EnsureSuccessStatusCode();e lascia che sia il server a occuparsene.
    • Perché non controllare semplicemente i log degli errori del server quando si verifica un errore interno del server?
  • Richiede la lettura della Contentproprietà prima di controllare lo stato. Possono esserci situazioni in cui ciò non è desiderabile, una delle quali è l'inefficienza.

Utilizzo

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)
                );
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.