¿Cómo obtener el código de estado de webclient?

90

Estoy usando la WebClientclase para publicar algunos datos en un formulario web. Me gustaría obtener el código de estado de respuesta del envío del formulario. Hasta ahora he descubierto cómo obtener el código de estado si hay una excepción

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

Sin embargo, si el formulario se envía correctamente y no se lanza ninguna excepción, entonces no sabré el código de estado (200,301,302, ...)

¿Hay alguna forma de obtener el código de estado cuando no se lanzan excepciones?

PD: prefiero no usar httpwebrequest / httpwebresponse

julio
fuente

Respuestas:

23

Probado. ResponseHeaders no incluye código de estado.

Si no me equivoco, WebClientes capaz de abstraer varias solicitudes distintas en una sola llamada de método (por ejemplo, manejar correctamente 100 respuestas Continuar, redirecciones y similares). Sospecho que sin usar HttpWebRequesty HttpWebResponse, un código de estado distinto puede no estar disponible.

Se me ocurre que, si no está interesado en los códigos de estado intermedios, puede asumir con seguridad que el código de estado final está en el rango 2xx (exitoso); de lo contrario, la llamada no sería exitosa.

Lamentablemente, el código de estado no está presente en el ResponseHeadersdiccionario.

kbrimington
fuente
2
parece que la única forma sería webrequest / response
julio
1
Parece un problema si está buscando explícitamente algún otro mensaje de la serie 200 (es decir, 201 CREADO; consulte: w3.org/Protocols/rfc2616/rfc2616-sec10.html ). : - / Sería bueno si estuviera disponible explícitamente incluso si se omitieran los "intermedios".
Norman H
1
@NormanH, no estoy en desacuerdo. Parecería que WebClient es un poco una abstracción con fugas cuando se trata de códigos de estado. ¡Salud!
kbrimington
87

Puede verificar si el error es de tipo WebExceptiony luego inspeccionar el código de respuesta;

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..
}
Henrik Hartz
fuente
Muchas gracias por esta respuesta que me indica la forma correcta de obtener encabezados de respuesta: desde WebException, no desde WebClient.ResponseHeaders.
Hong
1
sí, el mejor enfoque es leer los datos de respuesta en un bloque try catch y capturar WebException
Henrik Hartz
2
Me falta algo aquí. Ni 'System.Exception' ni 'System.Net.Exception' contienen una definición de 'Error'
Greg Woods
13
No habrá excepción si la llamada es exitosa (es decir, devuelve 2xx o 3xx). El cartel original buscaba 3xx, yo busco 204, otras personas buscan 201. Esto no responde a la pregunta formulada.
Simon Brooke
4
No estoy seguro de cómo esta respuesta se votó a favor hasta ahora cuando el cartel original escribió: "¿Hay alguna forma de obtener el código de estado cuando no se lanzan excepciones?" Supongo que no tiene sentido votar en contra ahora.
Frog Pr1nce
33

Hay una forma de hacerlo mediante la reflexión. Funciona con .NET 4.0. Tiene acceso a un campo privado y puede que no funcione en otras versiones de .NET sin modificaciones.

No tengo idea de por qué Microsoft no expuso este campo con una propiedad.

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.
fuente
2
FWIW, esto no es posible en Windows Phone que no permite el acceso a miembros privados incluso a través de la reflexión
Brendan
Tenga en cuenta que BindingFlags requiere "using System.Reflection;"
dlchambers
Bien, pero ¿hay alguna manera de obtener SubStatusCode? Por ejemplo, 403.1 o 403.2?
Roni Tovi
El objeto de respuesta tiene una propiedad SubStatusCode. msdn.microsoft.com/en-us/library/…
Dmitry S.
29

Si está utilizando .Net 4.0 (o menos):

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 está utilizando .Net 4.5.X o más reciente, cambie a HttpClient :

var response = await client.GetAsync("http://www.contoso.com/");
var statusCode = response.StatusCode;
Erik Philips
fuente
No funciona en Windows Phone: GetWebResponse () solo existe en versión de dos parámetros. Todavía +1.
Seva Alekseyev
Interesante que no funcione. ¡Me alegro de que tu respuesta funcione!
Erik Philips
Funcionó para mí, donde el reflejo en las respuestas más altas no lo hizo (aplicación .NET 4.5 para Windows 7 y 10)
David Shields
9

La respuesta de Erik no funciona en Windows Phone como está. Lo siguiente hace:

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

Al menos lo hace cuando se usa OpenReadAsync; para otros xxxAsyncmétodos, se recomienda encarecidamente realizar pruebas cuidadosas. El marco llama a GetWebResponse en algún lugar de la ruta del código; todo lo que hay que hacer es capturar y almacenar en caché el objeto de respuesta.

El código de reserva es 200 en este fragmento porque los errores HTTP genuinos (500, 404, etc.) se informan como excepciones de todos modos. El propósito de este truco es capturar códigos que no sean de error, en mi caso específico 304 (No modificado). Entonces, el respaldo asume que si el código de estado no está disponible de alguna manera, al menos no es erróneo.

Seva Alekseyev
fuente
3

Deberías usar

if (e.Status == WebExceptionStatus.ProtocolError)
{
   HttpWebResponse response = (HttpWebResponse)ex.Response;             
   if (response.StatusCode == HttpStatusCode.NotFound)
      System.Diagnostics.Debug.WriteLine("Not found!");
}
LeMoussel
fuente
3
Esto fue votado a favor ¿por qué? El OP dice claramente: However if the form is submitted successfully and no exception is thrown...
Kenneth K.
2

Esto es lo que uso para expandir la funcionalidad de WebClient. StatusCode y StatusDescription siempre contendrán el código / descripción de respuesta más reciente.

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

Por lo tanto, puede hacer una publicación y obtener un resultado a través de:

            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
fuente
Esto funcionó muy bien para mí, ya que estaba buscando el código de respuesta. ¡Buena solución!
evilfish
Tenga en cuenta que [a diferencia de HttpClient] las respuestas 4xx y 5xx dan como resultado una WebException que se lanza en "response = base.GetWebResponse (request);" línea. Puede extraer el estado y la respuesta de la excepción (si existen).
mwardm
Si. Todavía tiene que detectar excepciones como de costumbre. Sin embargo, si no hay una excepción, esto expone lo que quería el OP.
DFTR
1

En caso de que alguien más necesite una versión F # del truco descrito anteriormente.

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

Debería poder utilizar la llamada "client.ResponseHeaders [..]". Consulte este enlace para ver ejemplos de cómo recuperar cosas de la respuesta.

Paul Hadfield
fuente
1
los encabezados de respuesta devueltos son los encabezados del servidor como servidor, fecha, pragma, etc. pero sin código de estado (200,301,404 ...)
julio
1
Lo siento, me sorprendió un poco descubrir que no se devolvió.
Paul Hadfield
-1

Puede probar este código para obtener el código de estado HTTP de WebException o de OpenReadCompletedEventArgs.Error. También funciona en Silverlight porque SL no tiene definido 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
fuente