Penggunaan EnsureSuccessStatusCode dan penanganan HttpRequestException yang dilontarkannya

104

Apa pola penggunaannya HttpResponseMessage.EnsureSuccessStatusCode()? Ini membuang Isi pesan dan melempar HttpRequestException, tapi saya gagal untuk melihat bagaimana menanganinya secara terprogram berbeda dari generik Exception. Misalnya, ini tidak termasuk HttpStatusCode, yang akan berguna.

Apakah ada cara untuk mendapatkan lebih banyak info darinya? Adakah yang bisa menunjukkan pola penggunaan yang relevan dari keduanya EnsureSuccessStatusCode()dan HttpRequestException?

G. Stoynev
sumber

Jawaban:

156

Penggunaan idiomatik dari EnsureSuccessStatusCodeadalah untuk memverifikasi keberhasilan permintaan secara singkat, saat Anda tidak ingin menangani kasus kegagalan dengan cara tertentu. Ini sangat berguna ketika Anda ingin membuat prototipe klien dengan cepat.

Saat Anda memutuskan ingin menangani kasus kegagalan dengan cara tertentu, jangan lakukan hal berikut.

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

Ini melempar pengecualian hanya untuk segera menangkapnya, yang tidak masuk akal. The IsSuccessStatusCodemilik HttpResponseMessageada untuk tujuan ini. Lakukan hal berikut sebagai gantinya.

var response = await client.GetAsync(...);
if (response.IsSuccessStatusCode)
{
    // Handle success
}
else
{
    // Handle failure
}
Timothy Shields
sumber
1
Apakah ada cara untuk mendapatkan kode status integer yang sebenarnya ? ketika saya mencoba ini saya mendapatkan string seperti "NotFound", bukan kode status 404.
NickG
12
@NickG (int)response.StatusCode(Lihat msdn.microsoft.com/en-us/library/… )
Timothy Shields
1
Catatan, HttpRequestException default yang ditampilkan oleh EnsureSuccessStatusCode () akan memiliki frasa alasan. Tetapi Anda tetap dapat mengakses properti itu dalam tanggapannya jika tidak berhasil.
Stefan Zvonar
@StefanZvonar Saya tidak dapat menemukan Frase Alasan dalam pengecualian seperti yang Anda tulis.
KansaiRobot
1
@NickG Anda dapat menggunakan (int) response.StatusCode untuk mendapatkan nilai numerik untuk Kode Status HTTP
Henrik Holmgaard Høyer
95

Saya tidak suka EnsureSuccessStatusCode karena tidak mengembalikan apa pun yang berarti. Itulah mengapa saya membuat ekstensi saya sendiri:

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

kode sumber untuk EnsureSuccessStatusCode Microsoft dapat ditemukan di sini

Versi sinkron berdasarkan tautan 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);
}

Apa yang saya tidak suka tentang IsSuccessStatusCode adalah bahwa itu tidak dapat digunakan kembali dengan "baik". Misalnya Anda dapat menggunakan library seperti polly untuk mengulangi permintaan jika terjadi masalah jaringan. Dalam hal ini Anda memerlukan kode Anda untuk memunculkan pengecualian sehingga polly atau beberapa pustaka lain dapat menanganinya ...

piyama
sumber
1
setuju, kode default kehilangan fitur untuk mendapatkan pesan yang berarti dari kembali.
LT
2
Versi Anda berfungsi berbeda dari implementasi asli EnsureSuccessStatusCode. Anda selalu membuang response.Content(karena akhirnya dipanggil selalu bahkan setelah return;pernyataan) dan itu menghancurkan konten untuk dibaca lebih lanjut. Implementasi asli membuang konten hanya jika kode status tidak menunjukkan hasil yang sukses.
Lukas.Navratil
4
Saya tidak mengerti mengapa Anda terlebih dahulu await response.Content.ReadAsStringAsync()dan kemudian memeriksaif (response.Content != null)
mafu
3
Polly sekarang menangani hasil pengembalian serta pengecualian, tepatnya untuk membantu skenario semacam ini. Anda dapat mengkonfigurasi Polly untuk melindungi HttpRequestpanggilan, dan mengkonfigurasi kebijakan untuk menangani pengecualian tertentu, dan beberapa pengecualian HttpResponseCode. Lihat contoh di readme Polly di sini
penjelajah gunung
2
Bagaimana bisa response.Contentmenjadi null ketika itu baru saja memiliki metode yang dipanggil?
Ian Warburton
1

Saya menggunakan EnsureSuccessStatusCode ketika saya tidak ingin menangani Exception pada metode yang sama.

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

Pengecualian yang ditampilkan di GetUserIdAsync akan ditangani di DoSomethingAsync.

Sérgio Damasceno
sumber
0

Di bawah ini adalah solusi yang saya usulkan. Satu-satunya kekurangan adalah karena manajer sumber daya kerangka ASP.NET Core bersifat internal ke kerangka kerja, saya tidak dapat secara langsung menggunakan kembali string pesan internasionalisasi Microsoft, jadi saya hanya menggunakan pesan bahasa Inggris verbatim literal di sini.

Pro

  • Mencatat konten untuk kesalahan server 5xx
    • Terkadang, kesalahan server sebenarnya adalah kesalahan klien yang disamarkan, seperti klien yang menggunakan titik akhir yang tidak digunakan lagi yang akhirnya dimatikan.
  • Memudahkan untuk menemukan kesalahan saat menulis pengujian integrasi menggunakan ConfigureTestContainer<T>

Kontra

  • Tidak efisien.
    • Jika Anda membaca konten respons, dan kontennya sangat panjang, Anda akan memperlambat klien. Untuk beberapa klien, dengan persyaratan respons real-time yang lembut, gangguan ini mungkin tidak dapat diterima.
  • Tanggung jawab yang salah untuk kesalahan logging dan pemantauan.
    • Jika ini adalah kesalahan server 5xx, mengapa klien peduli, karena klien tidak melakukan kesalahan apa pun? Panggil saja response.EnsureSuccessStatusCode();dan biarkan server menanganinya.
    • Mengapa tidak hanya memeriksa log kesalahan server ketika ada Kesalahan Server Internal?
  • Membutuhkan membaca Contentproperti sebelum memeriksa status. Mungkin ada situasi di mana hal ini tidak diinginkan, salah satunya adalah inefisiensi.

Pemakaian

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
sumber