.Net HttpWebRequest.GetResponse () memunculkan pengecualian ketika kode status http 400 (permintaan buruk) dikembalikan

205

Saya berada dalam situasi di mana ketika saya mendapatkan kode HTTP 400 dari server, itu adalah cara yang sepenuhnya legal dari server memberi tahu saya apa yang salah dengan permintaan saya (menggunakan pesan dalam konten respons HTTP)

Namun, .NET HttpWebRequest memunculkan pengecualian ketika kode statusnya adalah 400.

Bagaimana saya menangani ini? Bagi saya 400 adalah sepenuhnya legal, dan agak membantu. Konten HTTP memiliki beberapa informasi penting tetapi pengecualian membuat saya keluar dari jalur saya.

tukang masak
sumber
6
Saya mengalami hal yang sama. Saya mengirimkan saran ke tim .NET Framework. Jangan ragu untuk memilihnya: connect.microsoft.com/VisualStudio/feedback/details/575075/…
Jonas Stawski

Jawaban:

344

Akan lebih baik jika ada beberapa cara mematikan "membuang kode yang tidak berhasil" tetapi jika Anda menangkap WebException Anda setidaknya dapat menggunakan respons:

using System;
using System.IO;
using System.Web;
using System.Net;

public class Test
{
    static void Main()
    {
        WebRequest request = WebRequest.Create("http://csharpindepth.com/asd");
        try
        {
            using (WebResponse response = request.GetResponse())
            {
                Console.WriteLine("Won't get here");
            }
        }
        catch (WebException e)
        {
            using (WebResponse response = e.Response)
            {
                HttpWebResponse httpResponse = (HttpWebResponse) response;
                Console.WriteLine("Error code: {0}", httpResponse.StatusCode);
                using (Stream data = response.GetResponseStream())
                using (var reader = new StreamReader(data))
                {
                    string text = reader.ReadToEnd();
                    Console.WriteLine(text);
                }
            }
        }
    }
}

Anda mungkin ingin merangkum bit "dapatkan saya respons meskipun itu bukan kode sukses" dalam metode terpisah. (Saya sarankan Anda masih melempar jika tidak ada respons, misalnya jika Anda tidak dapat terhubung.)

Jika respons kesalahan mungkin besar (yang tidak biasa), Anda mungkin ingin mengubah HttpWebRequest.DefaultMaximumErrorResponseLengthuntuk memastikan Anda mendapatkan seluruh kesalahan.

Jon Skeet
sumber
5
Isi aliran dikembalikan oleh GetResponseStream () pada respons yang dilampirkan ke WebException hanya nama kode status (mis. "Permintaan Buruk") alih-alih respons yang sebenarnya dikembalikan oleh server. Apakah ada cara untuk mendapatkan informasi ini?
Mark Watts
@MarkWatts: Seharusnya apa pun yang dikembalikan oleh server, dan telah dalam setiap situasi yang saya lihat. Bisakah Anda mereproduksi ini dengan URL eksternal tertentu? Saya sarankan Anda mengajukan pertanyaan baru (merujuk pada yang ini) dan menunjukkan apa yang terjadi.
Jon Skeet
Ternyata itu hanya melakukan ini ketika panjang konten dari jawaban adalah nol; itu menambahkan deskripsi tekstual dari kode status HTTP - 400 hanya "Permintaan Buruk" tetapi beberapa yang lain lebih deskriptif.
Mark Watts
Jika ada yang tahu tentang pembungkus yang bagus untuk kelas ini, berikan tautan ke sini. System.Net.WebClient berperilaku dengan cara yang sama dengan menerapkan sistem penanganan pengecualian.
John K
1
@AnkushJain: Saya percaya ini akan kembali secara normal untuk 2XX; pengalihan dapat terjadi untuk 3XX tergantung pada konfigurasi - Saya mengharapkan hal lain menyebabkan pengecualian, meskipun saya bisa saja salah. (Untuk 4XX, mungkin saja ia akan menerapkan informasi auth jika memilikinya tetapi belum menggunakannya.)
Jon Skeet
48

Saya tahu ini sudah dijawab sejak lama, tetapi saya membuat metode penyuluhan untuk membantu orang lain yang datang ke pertanyaan ini.

Kode:

public static class WebRequestExtensions
{
    public static WebResponse GetResponseWithoutException(this WebRequest request)
    {
        if (request == null)
        {
            throw new ArgumentNullException("request");
        }

        try
        {
            return request.GetResponse();
        }
        catch (WebException e)
        {
            if (e.Response == null)
            {
                throw;
            }

            return e.Response;
        }
    }
}

Pemakaian:

var request = (HttpWebRequest)WebRequest.CreateHttp("http://invalidurl.com");

//... (initialize more fields)

using (var response = (HttpWebResponse)request.GetResponseWithoutException())
{
    Console.WriteLine("I got Http Status Code: {0}", response.StatusCode);
}
Matius
sumber
2
WebException.Responsebisa dan mungkin null. Anda harus memikirkan kembali jika ini masalahnya.
Ian Kemp
@ Davidvid Saya setuju, penggunaannya sedikit janky, meskipun untuk sebagian besar hal, Anda mungkin harus menggunakan HttpClient, itu jauh lebih dapat dikonfigurasi, dan saya percaya itu adalah cara masa depan.
Matius
Apakah memeriksa permintaan == null benar-benar diperlukan? Karena ini adalah metode ekstensi, mencoba menggunakannya pada objek nol harus melempar pengecualian referensi nol sebelum menyentuh kode metode ekstensi ...... kan?
kwill
@kwill Metode ekstensi hanya metode statis, melakukan validasi input yang benar harus dilakukan untuk menghindari kebingungan, terutama ketika mereka tidak dipanggil dengan sintaksis metode ekstensi. Juga, ini adalah pendekatan konsisten yang dilakukan oleh tim .NET: github.com/dotnet/corefx/blob/…
Matius
1
Maaf saya salah mengerti pertanyaan kedua Anda. ((WebRequest) null).GetResponseWithoutException()sebenarnya tidak akan menyebabkan a NullReferenceException, karena dikompilasi dengan yang setara WebRequestExtensions.GetResponseWithoutException(null), yang tidak akan menghasilkan NullReferenceException, maka kebutuhan untuk validasi input.
Matius
13

Menariknya, HttpWebResponse.GetResponseStream()yang Anda dapatkan dari WebException.Responsetidak sama dengan aliran respons yang akan Anda terima dari server. Di lingkungan kami, kami kehilangan respons server yang sebenarnya ketika kode status HTTP 400 dikembalikan ke klien menggunakan HttpWebRequest/HttpWebResponseobjek. Dari apa yang telah kita lihat, aliran respons yang terkait dengan WebException's HttpWebResponsedihasilkan di klien dan tidak termasuk badan respons dari server. Sangat frustasi, karena kami ingin mengirim pesan kembali kepada klien alasan permintaan yang buruk.

Christopher Bartling
sumber
Saya menggunakan metode KEPALA dan menyebabkan pengecualian ini tetapi ketika menggunakan GET tidak ada masalah. Apa masalah sebenarnya dengan metode HEAD?
Mohammad Afrashteh
12

Saya memiliki masalah serupa ketika mencoba terhubung ke layanan OAuth2 Google.

Saya akhirnya menulis POST secara manual, tidak menggunakan WebRequest, seperti ini:

TcpClient client = new TcpClient("accounts.google.com", 443);
Stream netStream = client.GetStream();
SslStream sslStream = new SslStream(netStream);
sslStream.AuthenticateAsClient("accounts.google.com");

{
    byte[] contentAsBytes = Encoding.ASCII.GetBytes(content.ToString());

    StringBuilder msg = new StringBuilder();
    msg.AppendLine("POST /o/oauth2/token HTTP/1.1");
    msg.AppendLine("Host: accounts.google.com");
    msg.AppendLine("Content-Type: application/x-www-form-urlencoded");
    msg.AppendLine("Content-Length: " + contentAsBytes.Length.ToString());
    msg.AppendLine("");
    Debug.WriteLine("Request");
    Debug.WriteLine(msg.ToString());
    Debug.WriteLine(content.ToString());

    byte[] headerAsBytes = Encoding.ASCII.GetBytes(msg.ToString());
    sslStream.Write(headerAsBytes);
    sslStream.Write(contentAsBytes);
}

Debug.WriteLine("Response");

StreamReader reader = new StreamReader(sslStream);
while (true)
{  // Print the response line by line to the debug stream for inspection.
    string line = reader.ReadLine();
    if (line == null) break;
    Debug.WriteLine(line);
}

Respons yang ditulis ke aliran respons berisi teks kesalahan spesifik yang Anda cari.

Secara khusus, masalah saya adalah bahwa saya meletakkan endline di antara potongan data yang disandikan dengan url. Ketika saya membawa mereka keluar, semuanya berhasil. Anda mungkin dapat menggunakan teknik serupa untuk terhubung ke layanan Anda dan membaca teks kesalahan respons aktual.

Jugglist
sumber
2
HttpWebRequest sangat kacau. Bahkan soket lebih mudah (karena mereka tidak menyembunyikan kesalahan dari Anda).
Agent_L
6

Coba ini (ini VB-Code :-):

Try

Catch exp As WebException
  Dim sResponse As String = New StreamReader(exp.Response.GetResponseStream()).ReadToEnd
End Try
Bernd
sumber
3

Versi fungsi ekstensi asinkron:

    public static async Task<WebResponse> GetResponseAsyncNoEx(this WebRequest request)
    {
        try
        {
            return await request.GetResponseAsync();
        }
        catch(WebException ex)
        {
            return ex.Response;
        }
    }
Jason Hu
sumber
0

Ini menyelesaikannya untuk saya:
https://gist.github.com/beccasaurus/929007/a8f820b153a1cfdee3d06a9c0a1d7ebfced8bb77

TL; DR:
Masalah:
localhost mengembalikan konten yang diharapkan, IP jarak jauh mengubah 400 konten menjadi "Permintaan Buruk"
Solusi:
Menambahkan <httpErrors existingResponse="PassThrough"></httpErrors>untuk web.config/configuration/system.webServermenyelesaikan ini untuk saya; sekarang semua server (lokal & jarak jauh) mengembalikan konten yang sama persis (dihasilkan oleh saya) terlepas dari alamat IP dan / atau kode HTTP yang saya kembalikan.

dlchambers
sumber