Come ottenere il codice di stato da webclient?


90

Sto usando la WebClientclasse per inviare alcuni dati a un modulo web. Vorrei ricevere il codice di stato della risposta dell'invio del modulo. Finora ho scoperto come ottenere il codice di stato se c'è un'eccezione

Catch wex As WebException
        If TypeOf wex.Response Is HttpWebResponse Then
          msgbox(DirectCast(wex.Response, HttpWebResponse).StatusCode)
            End If

Tuttavia, se il modulo viene inviato correttamente e non viene generata alcuna eccezione, non conoscerò il codice di stato (200,301,302, ...)

C'è un modo per ottenere il codice di stato quando non sono state generate eccezioni?

PS: preferisco non usare httpwebrequest / httpwebresponse

Risposte:


23

L'ho provato. ResponseHeaders non includono il codice di stato.

Se non sbaglio, WebClientè in grado di astrarre più richieste distinte in una singola chiamata di metodo (ad esempio, gestire correttamente 100 risposte continue, reindirizzamenti e simili). Sospetto che senza utilizzare HttpWebRequeste HttpWebResponse, un codice di stato distinto potrebbe non essere disponibile.

Mi viene in mente che, se non sei interessato ai codici di stato intermedi, puoi tranquillamente presumere che il codice di stato finale sia compreso nell'intervallo 2xx (riuscito), altrimenti la chiamata non andrebbe a buon fine.

Il codice di stato purtroppo non è presente nel ResponseHeadersdizionario.


2
sembra che l'unico modo sarebbe webrequest / response
julio

1
Sembra un problema se stai cercando esplicitamente qualche altro messaggio della serie 200 (es. 201 CREATO - Vedi: w3.org/Protocols/rfc2616/rfc2616-sec10.html ). : - / Sarebbe bello se fosse esplicitamente disponibile anche se quelli "intermedi" fossero saltati.
Norman H

1
@ NormanH, non sono in disaccordo. Sembrerebbe che WebClient sia un po 'un'astrazione che perde quando si tratta di codici di stato. Saluti!
kbrimington

87

È possibile verificare se l'errore è di tipo WebExceptione quindi esaminare il codice di risposta;

if (e.Error.GetType().Name == "WebException")
{
   WebException we = (WebException)e.Error;
   HttpWebResponse response = (System.Net.HttpWebResponse)we.Response;
   if (response.StatusCode==HttpStatusCode.NotFound)
      System.Diagnostics.Debug.WriteLine("Not found!");
}

o

try
{
    // send request
}
catch (WebException e)
{
    // check e.Status as above etc..
}

Grazie mille per questa risposta che mi indica il modo giusto per ottenere le intestazioni di risposta - da WebException, non da WebClient.ResponseHeaders.
Hong

1
sì, l'approccio migliore è in realtà leggere i dati di risposta in un blocco try catch e catturare WebException
Henrik Hartz

2
Mi manca qualcosa qui. Né "System.Exception" o "System.Net.Exception" contengono una definizione per "Errore"
Greg Woods

13
Non ci saranno eccezioni se la chiamata ha successo (cioè restituisce 2xx o 3xx). Il poster originale cercava 3xx, sto cercando 204, altre persone stanno cercando 201. Questo non risponde alla domanda posta.
Simon Brooke

4
Non sei sicuro di come questa risposta sia stata votata fino ad ora quando il poster originale ha scritto: "C'è un modo per ottenere il codice di stato quando non ci sono eccezioni?" Immagino che non abbia senso votare a favore ora.
Frog Pr1nce

33

C'è un modo per farlo usando la riflessione. Funziona con .NET 4.0. Accede a un campo privato e potrebbe non funzionare in altre versioni di .NET senza modifiche.

Non ho idea del motivo per cui Microsoft non abbia esposto questo campo con una proprietà.

private static int GetStatusCode(WebClient client, out string statusDescription)
{
    FieldInfo responseField = client.GetType().GetField("m_WebResponse", BindingFlags.Instance | BindingFlags.NonPublic);

    if (responseField != null)
    {
        HttpWebResponse response = responseField.GetValue(client) as HttpWebResponse;

        if (response != null)
        {
            statusDescription = response.StatusDescription;
            return (int)response.StatusCode;
        }
    }

    statusDescription = null;
    return 0;
}

2
FWIW, questo non è possibile su Windows Phone che non consente l'accesso a membri privati ​​anche attraverso la riflessione
Brendan

Notare che BindingFlags richiede "using System.Reflection;"
Camere dl

Bello, ma c'è un modo per ottenere SubStatusCode? Ad esempio 403.1 o 403.2?
Roni Tovi

L'oggetto risposta ha una proprietà SubStatusCode. msdn.microsoft.com/en-us/library/…
Dmitry S.

29

Se stai usando .Net 4.0 (o meno):

class BetterWebClient : WebClient
{
        private WebRequest _Request = null;

        protected override WebRequest GetWebRequest(Uri address)
        {
            this._Request = base.GetWebRequest(address);

            if (this._Request is HttpWebRequest)
            {
                ((HttpWebRequest)this._Request).AllowAutoRedirect = false;
            }

            return this._Request;
        } 

        public HttpStatusCode StatusCode()
        {
            HttpStatusCode result;

            if (this._Request == null)
            {
                throw (new InvalidOperationException("Unable to retrieve the status 
                       code, maybe you haven't made a request yet."));
            }

            HttpWebResponse response = base.GetWebResponse(this._Request) 
                                       as HttpWebResponse;

            if (response != null)
            {
                result = response.StatusCode;
            }
            else
            {
                throw (new InvalidOperationException("Unable to retrieve the status 
                       code, maybe you haven't made a request yet."));
            }

            return result;
        }
    }

Se stai usando .Net 4.5.X o più recente, passa a HttpClient :

var response = await client.GetAsync("http://www.contoso.com/");
var statusCode = response.StatusCode;

Non funziona su Windows Phone: GetWebResponse () esiste solo in versione a due parametri. Ancora +1.
Seva Alekseyev


Ha funzionato per me, dove il riflesso nelle risposte più alte non ha funzionato (app .NET 4.5 per Windows 7 e 10)
David Shields

9

La risposta di Erik non funziona su Windows Phone così com'è. Quanto segue fa:

class WebClientEx : WebClient
{
    private WebResponse m_Resp = null;

    protected override WebResponse GetWebResponse(WebRequest Req, IAsyncResult ar)
    {
        try
        {
            this.m_Resp = base.GetWebResponse(request);
        }
        catch (WebException ex)
        {
            if (this.m_Resp == null)
                this.m_Resp = ex.Response;
        }
        return this.m_Resp;
    }

    public HttpStatusCode StatusCode
    {
        get
        {
            if (m_Resp != null && m_Resp is HttpWebResponse)
                return (m_Resp as HttpWebResponse).StatusCode;
            else
                return HttpStatusCode.OK;
        }
    }
}

Almeno lo fa quando si utilizza OpenReadAsync; per altri xxxAsyncmetodi, un attento test sarebbe altamente raccomandato. Il framework chiama GetWebResponse da qualche parte lungo il percorso del codice; tutto ciò che è necessario fare è acquisire e memorizzare nella cache l'oggetto risposta.

Il codice di fallback è 200 in questo frammento perché gli errori HTTP autentici - 500, 404, ecc. - vengono comunque segnalati come eccezioni. Lo scopo di questo trucco è acquisire codici non di errore, nel mio caso specifico 304 (Non modificato). Quindi il fallback presume che se il codice di stato è in qualche modo non disponibile, almeno non è errato.


3

Dovresti usare

if (e.Status == WebExceptionStatus.ProtocolError)
{
   HttpWebResponse response = (HttpWebResponse)ex.Response;             
   if (response.StatusCode == HttpStatusCode.NotFound)
      System.Diagnostics.Debug.WriteLine("Not found!");
}

3
Questo è stato votato perché? L'OP afferma chiaramente: However if the form is submitted successfully and no exception is thrown...
Kenneth K.

2

Questo è ciò che utilizzo per espandere le funzionalità di WebClient. StatusCode e StatusDescription conterranno sempre il codice / descrizione della risposta più recente.

                /// <summary>
                /// An expanded web client that allows certificate auth and 
                /// the retrieval of status' for successful requests
                /// </summary>
                public class WebClientCert : WebClient
                {
                    private X509Certificate2 _cert;
                    public WebClientCert(X509Certificate2 cert) : base() { _cert = cert; }
                    protected override WebRequest GetWebRequest(Uri address)
                    {
                        HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
                        if (_cert != null) { request.ClientCertificates.Add(_cert); }
                        return request;
                    }
                    protected override WebResponse GetWebResponse(WebRequest request)
                    {
                        WebResponse response = null;
                        response = base.GetWebResponse(request);
                        HttpWebResponse baseResponse = response as HttpWebResponse;
                        StatusCode = baseResponse.StatusCode;
                        StatusDescription = baseResponse.StatusDescription;
                        return response;
                    }
                    /// <summary>
                    /// The most recent response statusCode
                    /// </summary>
                    public HttpStatusCode StatusCode { get; set; }
                    /// <summary>
                    /// The most recent response statusDescription
                    /// </summary>
                    public string StatusDescription { get; set; }
                }

Quindi puoi fare un post e ottenere risultati tramite:

            byte[] response = null;
            using (WebClientCert client = new WebClientCert())
            {
                response = client.UploadValues(postUri, PostFields);
                HttpStatusCode code = client.StatusCode;
                string description = client.StatusDescription;
                //Use this information
            }

Questo ha funzionato alla grande per me mentre stavo cercando il codice di risposta. Bella soluzione!
evilfish

Tieni presente che [a differenza di HttpClient] le risposte 4xx e 5xx provocano la generazione di una WebException in "response = base.GetWebResponse (request);" linea. È possibile estrarre lo stato e la risposta dall'eccezione (se esistono).
mwardm

Sì. Devi ancora rilevare le eccezioni come al solito. Tuttavia, se non c'è un'eccezione, questo espone ciò che voleva l'OP.
DFTR

1

Nel caso in cui qualcun altro abbia bisogno di una versione F # dell'hack descritto sopra.

open System
open System.IO
open System.Net

type WebClientEx() =
     inherit WebClient ()
     [<DefaultValue>] val mutable m_Resp : WebResponse

     override x.GetWebResponse (req: WebRequest ) =
        x.m_Resp <- base.GetWebResponse(req)
        (req :?> HttpWebRequest).AllowAutoRedirect <- false;
        x.m_Resp

     override x.GetWebResponse (req: WebRequest , ar: IAsyncResult  ) =
        x.m_Resp <- base.GetWebResponse(req, ar)
        (req :?> HttpWebRequest).AllowAutoRedirect <- false;
        x.m_Resp

     member x.StatusCode with get() : HttpStatusCode = 
            if not (obj.ReferenceEquals (x.m_Resp, null)) && x.m_Resp.GetType() = typeof<HttpWebResponse> then
                (x.m_Resp :?> HttpWebResponse).StatusCode
            else
                HttpStatusCode.OK

let wc = new WebClientEx()
let st = wc.OpenRead("http://www.stackoverflow.com")
let sr = new StreamReader(st)
let res = sr.ReadToEnd()
wc.StatusCode
sr.Close()
st.Close()

-1

Dovresti essere in grado di utilizzare la chiamata "client.ResponseHeaders [..]", vedi questo link per esempi di come recuperare materiale dalla risposta


1
le intestazioni di risposta restituite sono le intestazioni del server come server, data, pragma, ecc. ma nessun codice di stato (200,301,404 ...)
julio

1
Mi dispiace, sono stato un po 'sorpreso di scoprire che non è stato restituito.
Paul Hadfield,

-1

Puoi provare questo codice per ottenere il codice di stato HTTP da WebException o da OpenReadCompletedEventArgs.Error. Funziona anche in Silverlight perché SL non ha WebExceptionStatus.ProtocolError definito.

HttpStatusCode GetHttpStatusCode(System.Exception err)
{
    if (err is WebException)
    {
        WebException we = (WebException)err;
        if (we.Response is HttpWebResponse)
        {
            HttpWebResponse response = (HttpWebResponse)we.Response;
            return response.StatusCode;
        }
    }
    return 0;
}
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.