【问题标题】:How to get status code from webclient?如何从 webclient 获取状态码?
【发布时间】:2010-08-26 11:33:46
【问题描述】:

我正在使用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

【问题讨论】:

    标签: c# .net vb.net webclient


    【解决方案1】:

    您可以检查错误是否为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。
    • 是的,最好的方法实际上是在try catch块中读取响应数据并捕获WebException
    • 我在这里遗漏了一些东西。 “System.Exception”或“System.Net.Exception”都不包含“Error”的定义
    • 调用成功也不例外(即返回2xx或3xx)。原发帖人在找 3xx,我在找 204,其他人在找 201。这并没有回答所提出的问题。
    • 当原始发帖人写道:“在没有抛出异常的情况下,是否有某种方法可以获取状态码?”我想现在投反对票没有意义。
    【解决方案2】:

    有一种使用反射的方法。它适用于 .NET 4.0。它访问一个私有字段,未经修改可能无法在其他版本的 .NET 中工作。

    我不知道为什么微软没有用属性公开这个字段。

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

    【讨论】:

    • FWIW,这在 Windows Phone 上是不可能的,即使通过反射也不允许访问私有成员
    • 请注意,BindingFlags 需要“使用 System.Reflection;”
    • 很好,但是有没有办法获得 SubStatusCode ?例如 403.1 或 403.2 ?
    • 响应对象有一个 SubStatusCode 属性。 msdn.microsoft.com/en-us/library/…
    【解决方案3】:

    如果您使用的是 .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。
    • 有趣的是它不起作用。 Glad your answer does the trick!
    • 为我工作,在更高的答案中没有反映(.NET 4.5 windows 7 和 10 应用程序)
    【解决方案4】:

    试过了。 ResponseHeaders 不包含状态码。

    如果我没记错的话,WebClient 能够在单个方法调用中抽象出多个不同的请求(例如,正确处理 100 个继续响应、重定向等)。我怀疑如果不使用HttpWebRequestHttpWebResponse,可能无法使用不同的状态码。

    我突然想到,如果您对中间状态码不感兴趣,您可以放心地假设最终状态码在 2xx(成功)范围内,否则调用不会成功。

    很遗憾,ResponseHeaders 字典中没有状态码。

    【讨论】:

    • 似乎唯一的方法是 webrequest/response
    • 如果您明确地寻找其他一些 200 系列消息(即 201 已创建 - 请参阅:w3.org/Protocols/rfc2616/rfc2616-sec10.html),这似乎是一个问题。 :-/ 即使跳过了“中间”部分,如果它是明确可用的,那就太好了。
    • @NormanH,我不反对。当涉及到状态码时,WebClient 似乎有点抽象。干杯!
    【解决方案5】:

    Erik 的回答无法在 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;只需捕获并缓存响应对象即可。

    此 sn-p 中的后备代码为 200,因为真正的 HTTP 错误 - 500、404 等 - 无论如何都会被报告为异常。这个技巧的目的是捕获非错误代码,在我的特定情况下为 304(未修改)。所以回退假设如果状态码不可用,至少它是一个非错误的。

    【讨论】:

      【解决方案6】:

      你应该使用

      if (e.Status == WebExceptionStatus.ProtocolError)
      {
         HttpWebResponse response = (HttpWebResponse)ex.Response;             
         if (response.StatusCode == HttpStatusCode.NotFound)
            System.Diagnostics.Debug.WriteLine("Not found!");
      }
      

      【讨论】:

      • 这为什么被投了? OP 明确指出:However if the form is submitted successfully and no exception is thrown...
      【解决方案7】:

      这是我用来扩展 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
                  }
      

      【讨论】:

      • 这对我来说非常有用,因为我正在寻找响应代码。不错的解决方案!
      • 请注意 [与 HttpClient 不同] 4xx 和 5xx 响应会导致在“response = base.GetWebResponse(request);”处引发 WebException线。您可以从异常中提取状态和响应(如果存在)。
      • 是的。您仍然必须像平常一样捕获异常。但是,如果没有例外,这会暴露 OP 想要的。
      【解决方案8】:

      以防万一其他人需要上述 hack 的 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()
      

      【讨论】:

        【解决方案9】:

        您应该能够使用“client.ResponseHeaders[..]”调用,请参阅link,了解从响应中取回内容的示例

        【讨论】:

        • 返回的响应头是服务器头,如 server、date、pragma 等。但没有状态码(200,301,404...)
        • 很抱歉,发现没有返回有点意外。
        【解决方案10】:

        您可以尝试使用此代码从 WebException 或 OpenReadCompletedEventArgs.Error 获取 HTTP 状态代码。它也适用于 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;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-08-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-17
          • 2021-12-03
          • 2018-03-27
          • 2013-02-27
          相关资源
          最近更新 更多