Як отримати код стану від 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.