Wie erhalte ich den Statuscode vom Webclient?

90

Ich benutze die WebClientKlasse, um einige Daten in ein Webformular zu posten. Ich möchte den Antwortstatuscode der Formularübermittlung erhalten. Bisher habe ich herausgefunden, wie man den Statuscode erhält, wenn es eine Ausnahme gibt

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

Wenn das Formular jedoch erfolgreich gesendet wurde und keine Ausnahme ausgelöst wird, kenne ich den Statuscode (200, 301, 302, ...) nicht.

Gibt es eine Möglichkeit, den Statuscode abzurufen, wenn keine Ausnahmen ausgelöst werden?

PS: Ich bevorzuge es, httpwebrequest / httpwebresponse nicht zu verwenden

julio
quelle

Antworten:

23

Versuchte es. ResponseHeaders enthalten keinen Statuscode.

Wenn ich mich nicht irre, kann ich WebClientmehrere unterschiedliche Anforderungen in einem einzigen Methodenaufruf abstrahieren (z. B. korrekte Behandlung von 100 Fortsetzen von Antworten, Weiterleitungen und dergleichen). Ich vermute, dass ohne HttpWebRequestund HttpWebResponsemöglicherweise kein eindeutiger Statuscode verfügbar ist.

Wenn Sie nicht an Zwischenstatuscodes interessiert sind, können Sie davon ausgehen, dass der endgültige Statuscode im Bereich 2xx (erfolgreich) liegt. Andernfalls wäre der Anruf nicht erfolgreich.

Der Statuscode ist im ResponseHeadersWörterbuch leider nicht vorhanden .

kbrimington
quelle
2
es scheint , dass der einzige Weg , webrequest / Antwort wäre
julio
1
Scheint ein Problem zu sein, wenn Sie explizit nach einer anderen Nachricht der Serie 200 suchen (z. B. 201 CREATED - Siehe: w3.org/Protocols/rfc2616/rfc2616-sec10.html ). : - / Es wäre schön, wenn das explizit verfügbar wäre, auch wenn die "Zwischen" übersprungen würden.
Norman H
1
@NormanH, ich bin nicht anderer Meinung. Es scheint, dass WebClient eine undichte Abstraktion ist, wenn es um Statuscodes geht. Prost!
Kbrimington
87

Sie können überprüfen, ob der Fehler vom Typ ist, WebExceptionund dann den Antwortcode überprüfen.

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

oder

try
{
    // send request
}
catch (WebException e)
{
    // check e.Status as above etc..
}
Henrik Hartz
quelle
Vielen Dank für diese Antwort, die mich auf den richtigen Weg zum Abrufen von Antwortheadern hinweist - von WebException, nicht von WebClient.ResponseHeaders.
Hong
1
Ja, der beste Ansatz ist tatsächlich, die Antwortdaten in einem Try-Catch-Block zu lesen und WebException
Henrik Hartz
1
Mir fehlt hier etwas. Weder 'System.Exception' noch 'System.Net.Exception' enthalten eine Definition für 'Error'
Greg Woods
13
Es gibt keine Ausnahme, wenn der Anruf erfolgreich ist (dh 2xx oder 3xx zurückgibt). Das Originalplakat suchte nach 3xx, ich suche nach 204, andere Leute suchen nach 201. Dies beantwortet die gestellte Frage nicht.
Simon Brooke
4
Sie sind sich nicht sicher, wie diese Antwort bisher positiv bewertet wurde, als das Originalplakat schrieb: "Gibt es eine Möglichkeit, den Statuscode zu erhalten, wenn keine Ausnahmen ausgelöst werden?" Ich denke, es macht keinen Sinn, jetzt abzustimmen.
Frosch Pr1nce
33

Es gibt eine Möglichkeit, dies mithilfe von Reflexion zu tun. Es funktioniert mit .NET 4.0. Es greift auf ein privates Feld zu und funktioniert möglicherweise nicht in anderen Versionen von .NET ohne Änderungen.

Ich habe keine Ahnung, warum Microsoft dieses Feld nicht mit einer Eigenschaft verfügbar gemacht hat.

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.
quelle
2
FWIW, dies ist unter Windows Phone nicht möglich, was den Zugriff auf private Mitglieder auch durch Reflexion nicht erlaubt
Brendan
Beachten Sie, dass für BindingFlags "using System.Reflection" erforderlich ist.
Dlchambers
Schön, aber gibt es eine Möglichkeit, SubStatusCode zu bekommen? Zum Beispiel 403.1 oder 403.2?
Roni Tovi
Das Antwortobjekt verfügt über eine SubStatusCode-Eigenschaft. msdn.microsoft.com/en-us/library/…
Dmitry S.
29

Wenn Sie .Net 4.0 (oder weniger) verwenden:

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

Wenn Sie .Net 4.5.X oder höher verwenden, wechseln Sie zu HttpClient :

var response = await client.GetAsync("http://www.contoso.com/");
var statusCode = response.StatusCode;
Erik Philips
quelle
Funktioniert nicht unter Windows Phone - GetWebResponse () ist nur in zwei Paramertern verfügbar. Immer noch +1.
Seva Alekseyev
Interessant, dass es nicht funktioniert. Ich bin froh, dass deine Antwort den Trick macht!
Erik Philips
Arbeitete für mich, wo die Reflexion in höheren Antworten nicht (.NET 4.5 Windows 7 und 10 App)
David Shields
9

Eriks Antwort funktioniert unter Windows Phone nicht so wie sie ist. Folgendes tut:

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

Zumindest bei der Verwendung OpenReadAsync; Für andere xxxAsyncMethoden wird eine sorgfältige Prüfung dringend empfohlen. Das Framework ruft GetWebResponse irgendwo entlang des Codepfads auf. Alles, was Sie tun müssen, ist das Antwortobjekt zu erfassen und zwischenzuspeichern.

Der Fallback-Code in diesem Snippet ist 200, da echte HTTP-Fehler - 500, 404 usw. - ohnehin als Ausnahmen gemeldet werden. Der Zweck dieses Tricks besteht darin, fehlerfreie Codes zu erfassen, in meinem speziellen Fall 304 (nicht geändert). Der Fallback geht also davon aus, dass der Statuscode, wenn er irgendwie nicht verfügbar ist, zumindest nicht fehlerhaft ist.

Seva Alekseyev
quelle
3

Du solltest benutzen

if (e.Status == WebExceptionStatus.ProtocolError)
{
   HttpWebResponse response = (HttpWebResponse)ex.Response;             
   if (response.StatusCode == HttpStatusCode.NotFound)
      System.Diagnostics.Debug.WriteLine("Not found!");
}
LeMoussel
quelle
3
Dies wurde gewählt, warum? Das OP stellt klar fest: However if the form is submitted successfully and no exception is thrown...
Kenneth K.
2

Dies ist, was ich zum Erweitern der WebClient-Funktionalität verwende. StatusCode und StatusDescription enthalten immer den neuesten Antwortcode / die neueste Beschreibung.

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

So können Sie einen Beitrag verfassen und Ergebnisse erhalten über:

            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
quelle
Dies funktionierte hervorragend für mich, da ich nach dem Antwortcode suchte. Schöne Lösung!
Evilfish
Beachten Sie, dass [im Gegensatz zu HttpClient] 4xx- und 5xx-Antworten dazu führen, dass eine WebException bei "response = base.GetWebResponse (request)" ausgelöst wird. Linie. Sie können den Status und die Antwort aus der Ausnahme abrufen (falls vorhanden).
Mwardm
Ja. Sie müssen immer noch Ausnahmen wie gewohnt abfangen. Wenn es jedoch keine Ausnahme gibt, wird dadurch offengelegt, was das OP wollte.
DFTR
1

Nur für den Fall, dass jemand anderes eine F # -Version des oben beschriebenen Hacks benötigt.

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
quelle
-1

Sie sollten in der Lage sein, den Aufruf "client.ResponseHeaders [..]" zu verwenden. Unter diesem Link finden Sie Beispiele für das Zurückholen von Daten aus der Antwort

Paul Hadfield
quelle
1
Die zurückgegebenen Antwortheader sind die Serverheader wie Server, Datum, Pragma usw. aber kein Statuscode (200, 301, 404 ...)
Juli
1
Tut mir leid, war ein bisschen überrascht, dass das nicht zurückgegeben wurde.
Paul Hadfield
-1

Sie können diesen Code ausprobieren, um HTTP-Statuscode von WebException oder von OpenReadCompletedEventArgs.Error abzurufen. Es funktioniert auch in Silverlight, da in SL WebExceptionStatus.ProtocolError nicht definiert ist.

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
quelle