Jak uzyskać kod statusu od webclienta?

90

Używam WebClientklasy do wysyłania danych do formularza internetowego. Chciałbym otrzymać kod statusu odpowiedzi przesłanego formularza. Do tej pory dowiedziałem się, jak uzyskać kod statusu, jeśli jest wyjątek

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

Jeśli jednak formularz zostanie przesłany pomyślnie i nie zostanie zgłoszony żaden wyjątek, nie będę znać kodu statusu (200,301,302, ...)

Czy istnieje sposób uzyskania kodu stanu, gdy nie ma żadnych wyjątków?

PS: Wolę nie używać httpwebrequest / httpwebresponse

julio
źródło

Odpowiedzi:

23

Wypróbowałem to. ResponseHeaders nie zawierają kodu stanu.

Jeśli się nie mylę, WebClientjest w stanie wyodrębnić wiele różnych żądań w jednym wywołaniu metody (np. Poprawna obsługa 100 odpowiedzi Kontynuuj, przekierowań i tym podobnych). Podejrzewam, że bez użycia HttpWebRequesti HttpWebResponse, odrębny kod statusu może nie być dostępny.

Wydaje mi się, że jeśli nie jesteś zainteresowany pośrednimi kodami statusu, możesz bezpiecznie założyć, że ostateczny kod statusu jest w zakresie 2xx (pomyślne), w przeciwnym razie połączenie nie zakończy się pomyślnie.

Kod statusu niestety nie występuje w ResponseHeaderssłowniku.

kbrimington
źródło
2
wydaje się, że jedynym sposobem byłoby zapytanie internetowe / odpowiedź
lipiec
1
Wydaje się problem, jeśli jawnie szukasz jakiejś innej wiadomości z serii 200 (np. 201 CREATED - patrz: w3.org/Protocols/rfc2616/rfc2616-sec10.html ). : - / Byłoby miło, gdyby było to jawnie dostępne, nawet jeśli „pośrednie” zostałyby pominięte.
Norman H
1
@NormanH, nie zgadzam się. Wydawałoby się, że WebClient jest trochę nieszczelną abstrakcją, jeśli chodzi o kody statusu. Twoje zdrowie!
kbrimington
87

Możesz sprawdzić, czy błąd jest rodzaju, WebExceptiona następnie sprawdzić kod odpowiedzi;

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

lub

try
{
    // send request
}
catch (WebException e)
{
    // check e.Status as above etc..
}
Henrik Hartz
źródło
Wielkie dzięki za tę odpowiedź, która wskazuje mi właściwy sposób uzyskiwania nagłówków odpowiedzi - z WebException, a nie z WebClient.ResponseHeaders.
Hong
1
tak, najlepszym podejściem jest faktycznie odczytanie danych odpowiedzi w bloku try catch i złapanie WebException
Henrik Hartz
2
Coś tu brakuje. Ani „System.Exception”, ani „System.Net.Exception” nie zawiera definicji „błędu”
Greg Woods
13
Nie będzie wyjątku, jeśli połączenie się powiedzie (tj. Zwróci 2xx lub 3xx). Oryginalny plakat szukał 3xx, szukam 204, inni szukają 201. To nie odpowiada na zadane pytanie.
Simon Brooke
4
Nie jestem pewien, jak do tej pory głosowano za tą odpowiedzią, kiedy oryginalny plakat napisał: „Czy istnieje sposób na uzyskanie kodu statusu, gdy nie ma wyjątków?” Chyba nie ma sensu teraz głosować w dół.
Frog Pr1nce
33

Jest na to sposób za pomocą refleksji. Działa z .NET 4.0. Uzyskuje dostęp do pola prywatnego i może nie działać w innych wersjach .NET bez modyfikacji.

Nie mam pojęcia, dlaczego Microsoft nie ujawnił tego pola z właściwością.

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.
źródło
2
FWIW, nie jest to możliwe na Windows Phone, który nie pozwala na dostęp do prywatnych członków nawet przez refleksję
Brendan
Zauważ, że BindingFlags wymaga „using System.Reflection;”
dlchambers
Fajnie, ale czy istnieje sposób na uzyskanie SubStatusCode? Na przykład 403,1 czy 403,2?
Roni Tovi
Obiekt odpowiedzi ma właściwość SubStatusCode. msdn.microsoft.com/en-us/library/…
Dmitry S.
30

Jeśli używasz .Net 4.0 (lub mniej):

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

Jeśli używasz .Net 4.5.X lub nowszego, przełącz się na HttpClient :

var response = await client.GetAsync("http://www.contoso.com/");
var statusCode = response.StatusCode;
Erik Philips
źródło
Nie działa na Windows Phone - GetWebResponse () istnieje tylko w wersji z dwoma paramerami. Wciąż +1.
Seva Alekseyev
Ciekawe, że to nie działa. Cieszę się, że Twoja odpowiedź załatwia sprawę!
Erik Philips,
U mnie zadziałało, gdzie odbicie w wyższych odpowiedziach nie zadziałało (aplikacja .NET 4.5 dla systemu Windows 7 i 10)
David Shields
9

Odpowiedź Erika nie działa na Windows Phone, tak jak jest. Następujące czynności:

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

Przynajmniej tak się dzieje podczas używania OpenReadAsync; w przypadku innych xxxAsyncmetod wysoce zalecane byłoby dokładne przetestowanie. Framework wywołuje GetWebResponse gdzieś na ścieżce kodu; wszystko, co trzeba zrobić, to przechwycić i buforować obiekt odpowiedzi.

Kod rezerwowy to 200 w tym fragmencie, ponieważ prawdziwe błędy HTTP - 500, 404 itd. - i tak są zgłaszane jako wyjątki. Celem tej sztuczki jest wychwycenie kodów innych niż błędy, w moim konkretnym przypadku 304 (bez modyfikacji). Tak więc rezerwa zakłada, że ​​jeśli kod statusu jest w jakiś sposób niedostępny, to przynajmniej nie jest błędny.

Seva Alekseyev
źródło
3

Powinieneś użyć

if (e.Status == WebExceptionStatus.ProtocolError)
{
   HttpWebResponse response = (HttpWebResponse)ex.Response;             
   if (response.StatusCode == HttpStatusCode.NotFound)
      System.Diagnostics.Debug.WriteLine("Not found!");
}
LeMoussel
źródło
3
To zostało przegłosowane, dlaczego? OP wyraźnie stwierdza: However if the form is submitted successfully and no exception is thrown...
Kenneth K.
2

To jest to, czego używam do rozszerzania funkcjonalności WebClient. StatusCode i StatusDescription zawsze będą zawierać najnowszy kod / opis odpowiedzi.

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

W ten sposób możesz napisać post i uzyskać wynik poprzez:

            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
źródło
To zadziałało świetnie, ponieważ szukałem kodu odpowiedzi. Niezłe rozwiązanie!
evilfish
Należy pamiętać, że [w przeciwieństwie do HttpClient] odpowiedzi 4xx i 5xx skutkują wyrzuceniem WebException na "response = base.GetWebResponse (żądanie);" linia. Możesz pobrać stan i odpowiedź z wyjątku (jeśli istnieją).
mwardm
Tak. Nadal musisz łapać wyjątki, tak jak zwykle. Jeśli jednak nie ma wyjątku, ujawnia to, czego chciał PO.
DFTR,
1

Na wypadek, gdyby ktoś inny potrzebował wersji F # wyżej opisanego hackowania.

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
źródło
-1

Powinieneś być w stanie użyć wywołania „client.ResponseHeaders [..]”, zobacz ten link, aby zapoznać się z przykładami odzyskiwania zawartości z odpowiedzi

Paul Hadfield
źródło
1
zwrócone nagłówki odpowiedzi to nagłówki serwera, takie jak serwer, data, pragma itp. ale żaden kod statusu (200301404 ...)
Julio
1
Przepraszam za to, byłem nieco zaskoczony, gdy okazało się, że nie został zwrócony.
Paul Hadfield
-1

Możesz wypróbować ten kod, aby uzyskać kod stanu HTTP z WebException lub z OpenReadCompletedEventArgs.Error. Działa również w Silverlight, ponieważ SL nie ma zdefiniowanego 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;
}
Siergiej
źródło