Як отримати код стану від webclient?


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словнику відсутній .


2
здається, єдиним способом буде веб-запит / відповідь
липень

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

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 вимагає "використання System.Reflection;"
dlchambers

Приємно, але чи є спосіб отримати SubStatusCode? Наприклад 403.1 або 403.2?
Роні Тові

Об'єкт відповіді має властивість SubStatusCode. msdn.microsoft.com/en-us/library/…
Дмитро С.

29

Якщо ви використовуєте .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!");
}

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
            }

Для мене це чудово працювало, оскільки я шукав код відповіді. Приємне рішення!
evilfish

Майте на увазі, що [на відміну від HttpClient] відповіді 4xx та 5xx призводять до викиду WebException на "response = base.GetWebResponse (запит);" лінія. Ви можете отримати статус та відповідь із винятку (якщо вони існують).
mwardm

Так. Вам все одно доведеться ловити винятки, як звичайно. Однак, якщо не існує винятку, це виявляє те, чого бажав ОП.
DFTR

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()

-1

Ви повинні мати можливість використовувати виклик "client.ResponseHeaders [..]", див. Це посилання для прикладів повернення матеріалів із відповіді


1
повернуті заголовки відповідей - це заголовки сервера, такі як сервер, дата, прагма тощо. але немає коду стану (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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.