.Net HttpWebRequest.GetResponse () solleva un'eccezione quando viene restituito il codice di stato http 400 (richiesta errata)


205

Sono in una situazione in cui quando ottengo un codice HTTP 400 dal server, è un modo completamente legale del server che mi dice che cosa non andava nella mia richiesta (usando un messaggio nel contenuto della risposta HTTP)

Tuttavia, .NET HttpWebRequest solleva un'eccezione quando il codice di stato è 400.

Come posso gestirlo? Per me un 400 è completamente legale e piuttosto utile. Il contenuto HTTP contiene alcune informazioni importanti ma l'eccezione mi butta fuori dal mio percorso.


6
Stavo vivendo la stessa cosa. Ho inviato un suggerimento al team di .NET Framework. Sentiti libero di votare per questo: connect.microsoft.com/VisualStudio/feedback/details/575075/…
Jonas Stawski

Risposte:


344

Sarebbe bello se ci fosse un modo per disattivare "buttare su codice non riuscito" ma se catturi WebException puoi almeno usare la risposta:

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

Potresti incapsulare il bit "fammi una risposta anche se non è un codice di successo" in un metodo separato. (Ti suggerisco di lanciare ancora se non c'è una risposta, ad esempio se non riesci a connetterti.)

Se la risposta all'errore può essere grande (il che è insolito), potresti voler modificare HttpWebRequest.DefaultMaximumErrorResponseLengthper assicurarti di ottenere l'intero errore.


5
Il contenuto del flusso restituito da GetResponseStream () sulla risposta allegata a WebException è solo il nome del codice di stato (ad esempio "Richiesta non valida") anziché la risposta effettivamente restituita dal server. C'è un modo per ottenere queste informazioni?
Mark Watts,

@MarkWatts: dovrebbe essere qualsiasi cosa sia stata restituita dal server ed è stata in ogni situazione che ho visto. Puoi riprodurlo con un particolare URL esterno? Ti suggerisco di porre una nuova domanda (riferendoti a questa) e mostrare cosa sta succedendo.
Jon Skeet,

Si scopre che lo fa solo quando la lunghezza del contenuto della risposta è zero; aggiunge una descrizione testuale del codice di stato HTTP - 400 è solo "Richiesta non valida" ma alcuni altri sono più descrittivi.
Mark Watts,

Se qualcuno conosce un buon wrapper per questa classe, lascia un link qui. System.Net.WebClient si comporta allo stesso modo invocando il sistema di gestione delle eccezioni.
John K,

1
@AnkushJain: credo che tornerà normalmente per 2XX; il reindirizzamento può avvenire per 3XX a seconda della configurazione - mi aspetto che qualcos'altro causi un'eccezione, anche se potrei sbagliarmi. (Per 4XX, è possibile che applicherà quindi le informazioni di autenticazione se ne ha, ma non le aveva già utilizzate.)
Jon Skeet,

48

So che è già stata data risposta molto tempo fa, ma ho creato un metodo di estensione per aiutare, spero, altre persone a rispondere a questa domanda.

Codice:

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

Uso:

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.Responsepuò e può essere null. In questo caso, dovresti ripetere il test.
Ian Kemp,

@DavidP Sono d'accordo, l'uso è un po 'stravagante, anche se per la maggior parte delle cose, probabilmente dovresti usare HttpClientinvece, è molto più configurabile e credo che sia la strada del futuro.
Matteo,

Il controllo per request == null è davvero necessario? Poiché si tratta di un metodo di estensione, il tentativo di utilizzarlo su un oggetto null dovrebbe generare un'eccezione di riferimento null prima che raggiunga il codice del metodo di estensione ...... giusto?
ucciderà

@kwill I metodi di estensione sono solo metodi statici, è necessario eseguire una corretta convalida dell'input per evitare confusione, soprattutto quando non vengono richiamati con la sintassi del metodo di estensione. Inoltre, questo è l'approccio coerente adottato dai team .NET: github.com/dotnet/corefx/blob/…
Matteo,

1
Mi dispiace di aver frainteso la tua seconda domanda. ((WebRequest) null).GetResponseWithoutException()in realtà non causerà a NullReferenceException, poiché è compilato con l'equivalente di WebRequestExtensions.GetResponseWithoutException(null), il che non comporterebbe a NullReferenceException, quindi la necessità di convalida dell'input.
Matteo

13

È interessante notare HttpWebResponse.GetResponseStream()che quello che ottieni dal WebException.Responsenon è lo stesso del flusso di risposta che avresti ricevuto dal server. Nel nostro ambiente, stiamo perdendo le risposte effettive del server quando un codice di stato 400 HTTP viene restituito al client utilizzando gli HttpWebRequest/HttpWebResponseoggetti. Da quello che abbiamo visto, il flusso di risposta associato al WebException's HttpWebResponseviene generato sul client e non include alcun corpo di risposta dal server. Molto frustrante, in quanto vogliamo rispedire al cliente il motivo della cattiva richiesta.


Uso il metodo HEAD e causa questa eccezione, ma quando uso GET non c'è nessun problema. Qual è esattamente il problema con il metodo HEAD?
Mohammad Afrashteh,

12

Ho riscontrato problemi simili durante il tentativo di connessione al servizio OAuth2 di Google.

Ho finito per scrivere il POST manualmente, non usando WebRequest, in questo modo:

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

La risposta che viene scritta nel flusso di risposta contiene il testo dell'errore specifico che stai cercando.

In particolare, il mio problema era che stavo mettendo fine ai pezzi di dati codificati in url. Quando li ho tolti, tutto ha funzionato. Potresti essere in grado di utilizzare una tecnica simile per connetterti al tuo servizio e leggere il testo dell'errore di risposta effettivo.


2
HttpWebRequest è così incasinato. Anche i socket sono più facili (perché non nascondono errori lontano da te).
Agent_L

6

Prova questo (è VB-Code :-):

Try

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

3

Una versione asincrona della funzione di estensione:

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

0

Questo mi ha risolto:
https://gist.github.com/beccasaurus/929007/a8f820b153a1cfdee3d06a9c0a1d7ebfced8bb77

TL; DR:
Problema:
localhost restituisce il contenuto previsto, l'IP remoto modifica 400 contenuti in "Richiesta errata"
Soluzione:
Aggiungendo <httpErrors existingResponse="PassThrough"></httpErrors>per web.config/configuration/system.webServerrisolvere questo problema per me; ora tutti i server (locali e remoti) restituiscono lo stesso identico contenuto (generato da me) indipendentemente dall'indirizzo IP e / o dal codice HTTP che restituisco.

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.