如何从网络客户端获取状态代码?

我使用 WebClient类发布一些数据到一个网络表单。我想得到表单提交的响应状态代码。到目前为止,我已经找到了如何获得状态代码,如果有一个异常

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

但是,如果表单成功提交,没有抛出异常,那么我将不知道状态代码(200,301,302,...)

当没有抛出异常时,是否有办法获得状态代码?

PS: 我不喜欢使用 httpwebrerequest/httpwebresponse

129738 次浏览

Tried it out. ResponseHeaders do not include status code.

If I'm not mistaken, WebClient is capable of abstracting away multiple distinct requests in a single method call (e.g. correctly handling 100 Continue responses, redirects, and the like). I suspect that without using HttpWebRequest and HttpWebResponse, a distinct status code may not be available.

It occurs to me that, if you are not interested in intermediate status codes, you can safely assume the final status code is in the 2xx (successful) range, otherwise, the call would not be successful.

The status code unfortunately isn't present in the ResponseHeaders dictionary.

You should be able to use the "client.ResponseHeaders[..]" call, see this link for examples of getting stuff back from the response

There is a way to do it using reflection. It works with .NET 4.0. It accesses a private field and may not work in other versions of .NET without modifications.

I have no idea why Microsoft did not expose this field with a property.

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

If you are using .Net 4.0 (or less):

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

If you are using .Net 4.5.X or newer, switch to HttpClient:

var response = await client.GetAsync("http://www.contoso.com/");
var statusCode = response.StatusCode;

You can check if the error is of type WebException and then inspect the response code;

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

or

try
{
// send request
}
catch (WebException e)
{
// check e.Status as above etc..
}

You should use

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

Erik's answer doesn't work on Windows Phone as is. The following does:

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

At least it does when using OpenReadAsync; for other xxxAsync methods, careful testing would be highly recommended. The framework calls GetWebResponse somewhere along the code path; all one needs to do is capture and cache the response object.

The fallback code is 200 in this snippet because genuine HTTP errors - 500, 404, etc - are reported as exceptions anyway. The purpose of this trick is to capture non-error codes, in my specific case 304 (Not modified). So the fallback assumes that if the status code is somehow unavailable, at least it's a non-erroneous one.

You can try this code to get HTTP status code from WebException or from OpenReadCompletedEventArgs.Error. It works in Silverlight too because SL does not have WebExceptionStatus.ProtocolError defined.

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

Just in case someone else needs an F# version of the above described hack.

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

This is what I use for expanding WebClient functionality. StatusCode and StatusDescription will always contain the most recent response code/description.

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

Thus you can do a post and get result 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
}