Como obter o código de status do webclient?

90

Estou usando a WebClientclasse para postar alguns dados em um formulário da web. Gostaria de obter o código de status de resposta do envio do formulário. Até agora, descobri como obter o código de status se houver uma exceção

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

No entanto, se o formulário for enviado com êxito e nenhuma exceção for lançada, não saberei o código de status (200.301.302, ...)

Existe alguma maneira de obter o código de status quando não há exceções lançadas?

PS: Prefiro não usar httpwebrequest / httpwebresponse

julio
fonte

Respostas:

23

Tentei. ResponseHeaders não incluem código de status.

Se não estou enganado, WebClienté capaz de abstrair várias solicitações distintas em uma única chamada de método (por exemplo, lidar corretamente com 100 respostas Continue, redirecionamentos e assim por diante). Suspeito que, sem usar HttpWebRequeste HttpWebResponse, um código de status distinto pode não estar disponível.

Ocorreu-me que, se você não estiver interessado em códigos de status intermediários, pode assumir com segurança que o código de status final está na faixa 2xx (bem-sucedida), caso contrário, a chamada não seria bem-sucedida.

O código de status infelizmente não está presente no ResponseHeadersdicionário.

Kbrimington
fonte
2
parece que a única maneira seria webrequest / response
julio
1
Parece um problema se você estiver procurando explicitamente por alguma outra mensagem da série 200 (ou seja, 201 CRIADA - Consulte: w3.org/Protocols/rfc2616/rfc2616-sec10.html ). : - / Seria bom se isso estivesse explicitamente disponível, mesmo se os "intermediários" fossem ignorados.
Norman H
1
@NormanH, eu não discordo. Parece que WebClient é um pouco uma abstração que vaza quando se trata de códigos de status. Felicidades!
kbrimington
87

Você pode verificar se o erro é do tipo WebExceptione, em seguida, inspecionar o código de resposta;

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
fonte
Muito obrigado por esta resposta que me aponta a maneira certa de obter cabeçalhos de resposta - de WebException, não de WebClient.ResponseHeaders.
Hong
1
sim, a melhor abordagem é realmente ler os dados de resposta em um bloco try catch e capturar WebException
Henrik Hartz
2
Estou faltando alguma coisa aqui. Nem 'System.Exception' ou 'System.Net.Exception' contém uma definição para 'Erro'
Greg Woods
13
Não haverá exceção se a chamada for bem-sucedida (ou seja, retornar 2xx ou 3xx). O autor da postagem original estava procurando 3xx, estou procurando 204, outras pessoas estão procurando 201. Isso não responde à pergunta feita.
Simon Brooke
4
Não tenho certeza de como essa resposta foi votada até agora quando o autor da postagem original escreveu: "Existe alguma maneira de obter o código de status quando não há exceções lançadas?" Acho que não há motivo para downvoting agora.
Frog Pr1nce 01 de
33

Existe uma maneira de fazer isso usando reflexão. Funciona com .NET 4.0. Ele acessa um campo privado e pode não funcionar em outras versões do .NET sem modificações.

Não tenho ideia de por que a Microsoft não expôs este campo com uma propriedade.

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.
fonte
2
FWIW, isso não é possível no Windows Phone, que não permite o acesso a membros privados mesmo através de reflexão
Brendan
Observe que BindingFlags requer "using System.Reflection;"
dlchambers de
Legal, mas há uma maneira de obter SubStatusCode? Por exemplo, 403.1 ou 403.2?
Roni Tovi
O objeto de resposta possui uma propriedade SubStatusCode. msdn.microsoft.com/en-us/library/…
Dmitry S.
29

Se você estiver usando .Net 4.0 (ou 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;
        }
    }

Se você estiver usando .Net 4.5.X ou mais recente, mude para HttpClient :

var response = await client.GetAsync("http://www.contoso.com/");
var statusCode = response.StatusCode;
Erik Philips
fonte
Não funciona no Windows Phone - GetWebResponse () só existe no tipo de dois parâmetros. Ainda +1.
Seva Alekseyev
Interessante que não funciona. Ainda bem que a sua resposta resolveu!
Erik Philips
Funcionou para mim, onde o reflexo nas respostas mais altas não funcionou (aplicativos .NET 4.5 windows 7 e 10)
David Shields
9

A resposta de Erik não funciona no Windows Phone como está. O seguinte faz:

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

Pelo menos faz ao usar OpenReadAsync; para outros xxxAsyncmétodos, um teste cuidadoso seria altamente recomendado. A estrutura chama GetWebResponse em algum lugar ao longo do caminho do código; tudo o que se precisa fazer é capturar e armazenar em cache o objeto de resposta.

O código substituto é 200 neste snippet porque erros HTTP genuínos - 500, 404, etc - são relatados como exceções de qualquer maneira. O objetivo deste truque é capturar códigos que não sejam de erro, no meu caso específico 304 (Não modificado). Portanto, o fallback assume que, se o código de status estiver de alguma forma indisponível, pelo menos ele não está errado.

Seva Alekseyev
fonte
3

Você deveria usar

if (e.Status == WebExceptionStatus.ProtocolError)
{
   HttpWebResponse response = (HttpWebResponse)ex.Response;             
   if (response.StatusCode == HttpStatusCode.NotFound)
      System.Diagnostics.Debug.WriteLine("Not found!");
}
LeMoussel
fonte
3
Isso foi votado por quê? O OP afirma claramente: However if the form is submitted successfully and no exception is thrown...
Kenneth K.
2

É o que eu uso para expandir a funcionalidade do WebClient. StatusCode e StatusDescription sempre conterão o código / descrição de resposta mais 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; }
                }

Assim, você pode fazer uma postagem e obter o resultado 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
fonte
Isso funcionou muito bem para mim, pois eu estava procurando o código de resposta. Ótima solução!
evilfish
Esteja ciente de que [ao contrário do HttpClient] as respostas 4xx e 5xx resultam em uma WebException sendo lançada no "response = base.GetWebResponse (request);" linha. Você pode obter o status e a resposta da exceção (se houver).
mwardm de
Sim. Você ainda tem que capturar exceções normalmente. No entanto, se não houver uma exceção, isso expõe o que o OP queria.
DFTR
1

Apenas no caso de alguém precisar de uma versão em F # do hack descrito acima.

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

Você deve ser capaz de usar a chamada "client.ResponseHeaders [..]", consulte este link para exemplos de como obter informações da resposta

Paul Hadfield
fonte
1
os cabeçalhos de resposta retornados são os cabeçalhos do servidor, como servidor, data, pragma, etc. mas sem código de status (200.301.404 ...)
julho de
1
Desculpe por isso, fiquei um pouco surpreso ao descobrir que não foi devolvido.
Paul Hadfield
-1

Você pode tentar este código para obter o código de status HTTP de WebException ou de OpenReadCompletedEventArgs.Error. Também funciona no Silverlight porque o SL não tem WebExceptionStatus.ProtocolError definido.

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
fonte