.Net HttpWebRequest.GetResponse (), http durum kodu 400 (hatalı istek) döndürüldüğünde istisna oluşturur


205

Sunucudan HTTP 400 kodu aldığımda, sunucunun isteğimle ilgili neyin yanlış olduğunu (HTTP yanıt içeriğinde bir mesaj kullanarak) anlatmanın tamamen yasal bir yolu olduğu bir durumdayım

Ancak, durum kodu 400 olduğunda .NET HttpWebRequest bir özel durum oluşturur.

Bunu nasıl halledebilirim? Benim için 400 tamamen yasal ve oldukça yararlı. HTTP içeriğinde bazı önemli bilgiler var ancak istisna beni yolumdan atıyor.


6
Aynı şeyi deneyimliyordum. .NET Framework ekibine bir öneri gönderdim. Oy vermek için çekinmeyin: connect.microsoft.com/VisualStudio/feedback/details/575075/…
Jonas Stawski

Yanıtlar:


347

"Başarılı olmayan kodu atma" özelliğini kapatmanın bir yolu olsaydı iyi olurdu, ancak WebException'ı yakalarsanız en azından yanıtı kullanabilirsiniz:

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

"Bana bir başarı kodu olmasa bile bir yanıt alın" bitini ayrı bir yöntemde kapsüllemek isteyebilirsiniz. (Bir yanıt yoksa hala atmanızı öneririm, örneğin bağlantı kuramadıysanız.)

Hata yanıtı büyükse (olağandışı) HttpWebRequest.DefaultMaximumErrorResponseLength, tüm hatayı aldığınızdan emin olmak için ince ayar yapmak isteyebilirsiniz .


5
WebException özel yanıtı GetResponseStream () tarafından döndürülen akış içeriği, sunucu tarafından gerçekten döndürülen yanıt yerine yalnızca durum kodunun adıdır (örn. "Kötü İstek"). Bu bilgiyi almanın herhangi bir yolu var mı?
Mark Watts

@ MarkWatts: Sunucu tarafından döndürülen her şey olmalı ve gördüğüm her durumda olmuştur. Bunu belirli bir harici URL ile çoğaltabilir misiniz? Yeni bir soru sormanızı (buna değinerek) ve neler olduğunu göstermenizi öneririm.
Jon Skeet

Bunu, yalnızca yanıtın içerik uzunluğu sıfır olduğunda yapar; HTTP durum kodunun metinsel bir açıklamasını ekler - 400 sadece "Kötü İstek" tir, ancak bazıları daha açıklayıcıdır.
Mark Watts

Herkes bu sınıf için güzel bir sarıcı biliyorsa, buraya bir bağlantı bırakın. System.Net.WebClient, kural dışı durum işleme sistemini çağırarak aynı şekilde davranır.
John K

1
@AnkushJain: 2XX için normal olarak döneceğine inanıyorum; yapılandırmaya bağlı olarak 3XX için yeniden yönlendirme olabilir - yanlış olsa da, başka bir şeyin istisnaya neden olmasını beklerdim. (4XX için, eğer varsa, ancak henüz kullanmadıysa kimlik bilgisi uygulayabilmesi mümkündür.)
Jon Skeet

48

Bunun uzun zaman önce yanıtlandığını biliyorum, ama umarım bu soruya gelen diğer insanlara yardım etmek için bir uzatma yöntemi yaptım.

Kod:

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

Kullanımı:

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

2
WebException.Responseolabilir ve olabilir null. Durum böyleyse yeniden düşünmelisiniz.
Ian Kemp

@DavidP Kabul ediyorum, kullanım biraz sarsıntılı olsa da, çoğu şey için muhtemelen HttpClientbunun yerine kullanmalısınız, çok daha yapılandırılabilir ve inanıyorum ki geleceğin yolu.
Matthew

İstek == null olup olmadığını denetlemek gerçekten gerekli mi? Bu bir uzantı yöntemi olduğundan, null nesnesinde kullanmaya çalışmak, uzantı yöntemi koduna çarpmadan önce bir null başvuru istisnası atmalıdır.
kwill

@kwill Genişletme yöntemleri yalnızca statik yöntemlerdir, özellikle de uzantı yöntemi sözdizimi ile çağrılmadığında karışıklığı önlemek için düzgün giriş doğrulaması yapmak gerekir. Ayrıca, bu .NET ekipleri tarafından yapılan tutarlı bir yaklaşımdır: github.com/dotnet/corefx/blob/…
Matthew

1
Üzgünüm, ikinci sorunuzu yanlış anladım. ((WebRequest) null).GetResponseWithoutException()aslında neden olmaz NullReferenceExceptionbunun eşdeğeri derlenmiştir, WebRequestExtensions.GetResponseWithoutException(null)bir neden olmaz olan NullReferenceException, giriş doğrulama dolayısıyla ihtiyaç devam etmektedir.
Matthew

13

İlginçtir, HttpWebResponse.GetResponseStream()sizi almak WebException.Responsesunucudan almış olacağını yanıt akışı olarak aynı değildir. Ortamımızda, 400 HTTP durum kodu HttpWebRequest/HttpWebResponsenesneleri kullanarak istemciye geri döndürüldüğünde gerçek sunucu yanıtlarını kaybediyoruz . Gördüğümüz kadarıyla, ile ilişkili yanıt akışı WebException's HttpWebResponseistemcide oluşturulur ve sunucudan gelen yanıt gövdesini içermez. Çok sinir bozucu, kötü istemcinin nedenini müşteriye geri göndermek istiyoruz.


HEAD yöntemini kullanıyorum ve bu özel duruma neden oluyorum ama GET kullanırken herhangi bir sorun yok. HEAD yöntemiyle ilgili sorun tam olarak nedir?
Mohammad Afrashteh

12

Google'ın OAuth2 hizmetine bağlanmaya çalışırken benzer sorunlar yaşadım.

Ben böyle WebRequest kullanarak değil, POST yazma sona erdi:

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

Yanıt akışına yazılan yanıt, peşinde olduğunuz belirli hata metnini içerir.

Özellikle, benim sorunum url kodlu veri parçaları arasına son satır koymak oldu. Onları çıkardığımda her şey işe yaradı. Hizmetinize bağlanmak ve gerçek yanıt hatası metnini okumak için benzer bir teknik kullanabilirsiniz.


2
HttpWebRequest çok karışık. Soketler bile daha kolaydır (çünkü hataları sizden uzak tutmazlar).
Agent_L

6

Bunu deneyin (bu VB-Kodu :-):

Try

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

3

Uzantı işlevinin eşzamansız bir sürümü:

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

0

Bu benim için çözdü:
https://gist.github.com/beccasaurus/929007/a8f820b153a1cfdee3d06a9c0a1d7ebfced8bb77

TL; DR:
Sorun:
localhost beklenen içeriği döndürüyor, uzak IP 400 içeriği "Kötü İstek" olarak değiştiriyor
Çözüm: Bunu benim için çözerek
ekleme ; şimdi tüm sunucular (yerel ve uzak), döndürdüğüm IP adresi ve / veya HTTP kodundan bağımsız olarak aynı içeriği (oluşturduğum) döndürüyor.<httpErrors existingResponse="PassThrough"></httpErrors>web.config/configuration/system.webServer

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.