Как получить код статуса от веб-клиента?

90

Я использую WebClientкласс для публикации некоторых данных в веб-форме. Я хочу получить код статуса ответа при отправке формы. Пока что узнал, как получить код статуса, если есть исключение

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

Однако, если форма отправлена ​​успешно и исключение не возникло, я не буду знать код состояния (200,301,302, ...)

Есть ли способ получить код состояния, когда не генерируются исключения?

PS: я предпочитаю не использовать httpwebrequest / httpwebresponse

Хулио
источник

Ответы:

23

Пробовал. ResponseHeaders не включают код состояния.

Если я не ошибаюсь, WebClientспособен абстрагироваться от нескольких отдельных запросов за один вызов метода (например, правильно обрабатывать 100 ответов Continue, перенаправления и т.п.). Я подозреваю, что без использования HttpWebRequestи HttpWebResponseотдельный код состояния может быть недоступен.

Мне пришло в голову, что, если вас не интересуют коды промежуточного статуса, вы можете с уверенностью предположить, что окончательный код статуса находится в диапазоне 2xx (успешный), иначе вызов не будет успешным.

К сожалению, кода состояния нет в ResponseHeadersсловаре.

Kbrimington
источник
2
кажется, что единственный способ - это webrequest / response
Хулио
1
Кажется проблемой, если вы явно ищете какое-то другое сообщение серии 200 (например, 201 СОЗДАНО - см .: w3.org/Protocols/rfc2616/rfc2616-sec10.html ). : - / Было бы хорошо, если бы это было явно доступно, даже если бы "промежуточные" были пропущены.
Norman H
1
@NormanH, я не возражаю. Казалось бы, WebClient - это немного ненадежная абстракция, когда дело касается кодов состояния. Ура!
kbrimington
87

Вы можете проверить тип ошибки, WebExceptionа затем проверить код ответа;

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

или

try
{
    // send request
}
catch (WebException e)
{
    // check e.Status as above etc..
}
Хенрик Харц
источник
Большое спасибо за этот ответ, который указывает мне правильный способ получения заголовков ответов - от WebException, а не от WebClient.ResponseHeaders.
Гонконг,
1
да, лучший подход на самом деле - прочитать данные ответа в блоке try catch и поймать WebException
Хенрик Харц
2
Я кое-что упустил. Ни «System.Exception», ни «System.Net.Exception» не содержат определения «Ошибка»
Грег Вудс,
13
Не будет никаких исключений, если вызов будет успешным (т.е. возвратит 2xx или 3xx). Первоначальный плакат искал 3xx, я ищу 204, другие ищут 201. Это не отвечает на заданный вопрос.
Саймон Брук,
4
Не знаю, как за этот ответ проголосовали до сих пор, когда исходный плакат написал: «Есть ли способ получить код состояния, когда не генерируются исключения?» Думаю, сейчас нет смысла голосовать против.
Frog Pr1nce 01
33

Есть способ сделать это с помощью отражения. Работает с .NET 4.0. Он обращается к частному полю и может не работать в других версиях .NET без изменений.

Я понятия не имею, почему Microsoft не предоставила это поле со свойством.

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;
}
Дмитрий С.
источник
2
FWIW, это невозможно на Windows Phone, который не позволяет получить доступ к закрытым членам даже через отражение
Брендан
Обратите внимание, что BindingFlags требует «using System.Reflection;»
dlchambers
Хорошо, но есть ли способ получить SubStatusCode? Например 403.1 или 403.2?
Roni Tovi
Объект ответа имеет свойство SubStatusCode. msdn.microsoft.com/en-us/library/…
Дмитрий С.
30

Если вы используете .Net 4.0 (или меньше):

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

Если вы используете .Net 4.5.X или новее, переключитесь на HttpClient :

var response = await client.GetAsync("http://www.contoso.com/");
var statusCode = response.StatusCode;
Эрик Филипс
источник
Не работает на Windows Phone - GetWebResponse () существует только с двумя параметрами. Тем не менее +1.
Сева Алексеев
Интересно, что это не работает. Рад, что ваш ответ помог!
Эрик Филипс
Сработал для меня, где отражение в более высоких ответах не было (приложение .NET 4.5 для Windows 7 и 10)
Дэвид Шилдс,
9

Ответ Эрика не работает на Windows Phone как есть. Следующее делает:

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

По крайней мере, при использовании OpenReadAsync; для другихxxxAsync методов настоятельно рекомендуется тщательное тестирование. Платформа вызывает GetWebResponse где-нибудь по пути кода; все, что нужно сделать, это захватить и кэшировать объект ответа.

Резервный код - 200 в этом фрагменте, потому что подлинные ошибки HTTP - 500, 404 и т. Д. - в любом случае сообщаются как исключения. Цель этого трюка - захватить коды ошибок, в моем конкретном случае 304 (без изменений). Таким образом, резервный вариант предполагает, что если код состояния каким-то образом недоступен, по крайней мере, он не ошибочный.

Сева Алексеев
источник
3

Вы должны использовать

if (e.Status == WebExceptionStatus.ProtocolError)
{
   HttpWebResponse response = (HttpWebResponse)ex.Response;             
   if (response.StatusCode == HttpStatusCode.NotFound)
      System.Diagnostics.Debug.WriteLine("Not found!");
}
LeMoussel
источник
3
Почему проголосовали за это? В ОП четко сказано: However if the form is submitted successfully and no exception is thrown...
Кеннет К.
2

Это то, что я использую для расширения функциональности WebClient. StatusCode и StatusDescription всегда будут содержать самый последний код / ​​описание ответа.

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

Таким образом, вы можете написать сообщение и получить результат через:

            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
источник
Это отлично сработало для меня, поскольку я искал код ответа. Хорошее решение!
evilfish
Имейте в виду, что [в отличие от HttpClient] ответы 4xx и 5xx приводят к возникновению исключения WebException в ответе = base.GetWebResponse (request); линия. Вы можете получить статус и ответ исключения (если они есть).
mwardm
Да. Вы по-прежнему должны ловить исключения, как обычно. Однако, если нет исключения, это раскрывает то, что хотел OP.
DFTR 05
1

На всякий случай кому-то понадобится F # версия описанного выше хака.

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
источник
-1

У вас должна быть возможность использовать вызов client.ResponseHeaders [..], см. Эту ссылку , чтобы увидеть примеры получения данных из ответа.

Пол Хэдфилд
источник
1
возвращаемые заголовки ответа - это заголовки сервера, такие как server, date, pragma и т. д. но нет кода статуса (200 301 404 ...)
Хулио
1
Сожалею об этом, был немного удивлен, обнаружив, что не вернулся.
Пол Хэдфилд,
-1

Вы можете попробовать этот код, чтобы получить код состояния HTTP из WebException или из OpenReadCompletedEventArgs.Error. Это работает и в Silverlight, потому что SL не имеет определенного 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;
}
Сергей
источник