Comment obtenir le code de statut du client Web?

90

J'utilise la WebClientclasse pour publier des données dans un formulaire Web. Je souhaite obtenir le code d'état de réponse de la soumission du formulaire. Jusqu'à présent, j'ai découvert comment obtenir le code d'état s'il y a une exception

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

Cependant, si le formulaire est soumis avec succès et qu'aucune exception n'est levée, je ne connais pas le code d'état (200, 301, 302, ...)

Existe-t-il un moyen d'obtenir le code d'état lorsqu'il n'y a pas d'exceptions levées?

PS: je préfère ne pas utiliser httpwebrequest / httpwebresponse

julio
la source

Réponses:

23

Je l'ai essayé. Les ResponseHeaders n'incluent pas de code d'état.

Si je ne me trompe pas, WebClientest capable d'abstraire plusieurs demandes distinctes en un seul appel de méthode (par exemple, gérer correctement 100 réponses Continue, redirections, etc.). Je soupçonne que sans utiliser HttpWebRequestet HttpWebResponse, un code d'état distinct peut ne pas être disponible.

Il me vient à l'esprit que, si les codes d'état intermédiaires ne vous intéressent pas, vous pouvez supposer en toute sécurité que le code d'état final est dans la plage 2xx (succès), sinon l'appel ne réussira pas.

Le code d'état n'est malheureusement pas présent dans le ResponseHeadersdictionnaire.

Kbrimington
la source
2
il semble que le seul moyen serait webrequest / response
julio
1
Cela semble poser un problème si vous recherchez explicitement un autre message de la série 200 (par exemple 201 CREATED - Voir: w3.org/Protocols/rfc2616/rfc2616-sec10.html ). : - / Ce serait bien si cela était explicitement disponible même si les "intermédiaires" étaient ignorés.
Norman H
1
@NormanH, je ne suis pas en désaccord. Il semblerait que WebClient soit un peu une abstraction qui fuit en ce qui concerne les codes d'état. À votre santé!
kbrimington
87

Vous pouvez vérifier si l'erreur est de type WebException, puis inspecter le code de réponse;

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

ou

try
{
    // send request
}
catch (WebException e)
{
    // check e.Status as above etc..
}
Henrik Hartz
la source
Merci beaucoup pour cette réponse qui m'indique la bonne façon d'obtenir les en-têtes de réponse - à partir de WebException, pas de WebClient.ResponseHeaders.
Hong
1
ouais, la meilleure approche consiste en fait à lire les données de réponse dans un bloc try catch et à attraper WebException
Henrik Hartz
2
Il me manque quelque chose ici. Ni «System.Exception» ni «System.Net.Exception» ne contiennent de définition pour «Error»
Greg Woods
13
Il n'y aura pas d'exception si l'appel réussit (c'est-à-dire renvoie 2xx ou 3xx). L'affiche originale recherchait 3xx, je recherche 204, d'autres recherchent 201. Cela ne répond pas à la question posée.
Simon Brooke
4
Je ne sais pas comment cette réponse a été votée jusqu'à présent lorsque l'affiche d'origine a écrit: "Y a-t-il un moyen d'obtenir le code d'état lorsqu'il n'y a pas d'exceptions?" Je suppose que cela ne sert à rien de voter maintenant.
Frog Pr1nce
33

Il existe un moyen de le faire en utilisant la réflexion. Cela fonctionne avec .NET 4.0. Il accède à un champ privé et peut ne pas fonctionner dans d'autres versions de .NET sans modifications.

Je n'ai aucune idée pourquoi Microsoft n'a pas exposé ce champ avec une propriété.

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;
}
Dmitry S.
la source
2
FWIW, ce n'est pas possible sur Windows Phone qui ne permet pas d'accéder aux membres privés même par réflexion
Brendan
Notez que BindingFlags nécessite "using System.Reflection;"
dlchambers le
Bien, mais y a-t-il un moyen d'obtenir SubStatusCode? Par exemple 403.1 ou 403.2?
Roni Tovi
L'objet de réponse a une propriété SubStatusCode. msdn.microsoft.com/en-us/library/…
Dmitry S.
29

Si vous utilisez .Net 4.0 (ou moins):

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

Si vous utilisez .Net 4.5.X ou plus récent, passez à HttpClient :

var response = await client.GetAsync("http://www.contoso.com/");
var statusCode = response.StatusCode;
Erik Philips
la source
Ne fonctionne pas sur Windows Phone - GetWebResponse () n'existe que dans une version à deux paramètres. Toujours +1.
Seva Alekseyev
Intéressant que cela ne fonctionne pas. Heureux que votre réponse fasse l'affaire!
Erik Philips
A travaillé pour moi, là où la réflexion dans les réponses plus élevées n'a pas fonctionné (application .NET 4.5 Windows 7 et 10)
David Shields
9

La réponse d'Erik ne fonctionne pas sur Windows Phone tel quel. Ce qui suit fait:

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

Au moins il le fait lors de l'utilisation OpenReadAsync; pour les autres xxxAsyncméthodes, des tests minutieux seraient fortement recommandés. Le framework appelle GetWebResponse quelque part le long du chemin du code; il suffit de capturer et de mettre en cache l'objet de réponse.

Le code de secours est 200 dans cet extrait de code car les véritables erreurs HTTP - 500, 404, etc. - sont de toute façon signalées comme des exceptions. Le but de cette astuce est de capturer les codes sans erreur, dans mon cas spécifique 304 (non modifié). Ainsi, la solution de secours suppose que si le code d'état est indisponible, au moins c'est un code non erroné.

Seva Alekseyev
la source
3

Tu devrais utiliser

if (e.Status == WebExceptionStatus.ProtocolError)
{
   HttpWebResponse response = (HttpWebResponse)ex.Response;             
   if (response.StatusCode == HttpStatusCode.NotFound)
      System.Diagnostics.Debug.WriteLine("Not found!");
}
LeMoussel
la source
3
Cela a été voté pourquoi? L'OP déclare clairement: However if the form is submitted successfully and no exception is thrown...
Kenneth K.
2

C'est ce que j'utilise pour étendre les fonctionnalités de WebClient. StatusCode et StatusDescription contiendront toujours le code / description de réponse le plus récent.

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

Ainsi, vous pouvez faire un post et obtenir un résultat via:

            byte[] response = null;
            using (WebClientCert client = new WebClientCert())
            {
                response = client.UploadValues(postUri, PostFields);
                HttpStatusCode code = client.StatusCode;
                string description = client.StatusDescription;
                //Use this information
            }
DFTR
la source
Cela a très bien fonctionné pour moi car je cherchais le code de réponse. Belle solution!
poisson maléfique
Sachez que [contrairement à HttpClient] les réponses 4xx et 5xx entraînent une WebException lancée à la "response = base.GetWebResponse (request);" ligne. Vous pouvez extraire l'état et la réponse de l'exception (s'ils existent).
mwardm
Oui. Vous devez toujours attraper des exceptions comme d'habitude. Cependant, s'il n'y a pas d'exception, cela expose ce que l'OP voulait.
DFTR
1

Juste au cas où quelqu'un d'autre aurait besoin d'une version F # du hack décrit ci-dessus.

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()
jpe
la source
-1

Vous devriez pouvoir utiliser l'appel "client.ResponseHeaders [..]", voir ce lien pour des exemples de récupération de données à partir de la réponse

Paul Hadfield
la source
1
les en-têtes de réponse renvoyés sont les en-têtes de serveur tels que serveur, date, pragma, etc. mais pas de code de statut (200301404 ...)
julio
1
Désolé à ce sujet, j'ai été un peu surpris de constater que cela n'a pas été retourné.
Paul Hadfield
-1

Vous pouvez essayer ce code pour obtenir le code d'état HTTP à partir de WebException ou d'OpenReadCompletedEventArgs.Error. Cela fonctionne également dans Silverlight car SL n'a pas défini WebExceptionStatus.ProtocolError.

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;
}
Sergey
la source