98

I am using the WebClient class to post some data to a web form. I would like to get the response status code of the form submission. So far I've found out how to get the status code if there is a exception

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

However if the form is submitted successfully and no exception is thrown then I won't know the status code(200,301,302,...)

Is there some way to get the status code when there is no exceptions thrown?

PS: I prefer not to use httpwebrequest/httpwebresponse

SteveC
  • 15,808
  • 23
  • 102
  • 173
julio
  • 1,069
  • 1
  • 7
  • 8

10 Answers10

95

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..
}
g t
  • 7,287
  • 7
  • 50
  • 85
Henrik Hartz
  • 3,677
  • 1
  • 27
  • 28
  • Thanks a lot for this answer which points me to the right way to get response headers - from WebException, not from WebClient.ResponseHeaders. – Hong Apr 14 '12 at 13:02
  • 1
    yeah, the best approach is actually to read the response data in a try catch block and catch WebException – Henrik Hartz Jun 15 '12 at 13:18
  • Typically you would just catch that type of exception first, then catch the more generic exceptions later. Rather than using an if(getType). Might be worth an edit? – MondayPaper May 01 '13 at 17:30
  • 2
    I'm missing something here. Neither 'System.Exception' or 'System.Net.Exception' contains a definition for 'Error' – Greg Woods Aug 22 '13 at 08:24
  • 15
    There will be no exception if the call is successful (i.e. returns 2xx or 3xx). The original poster was looking for 3xx, I am looking for 204, other people are looking for 201. This does not answer the question asked. – Simon Brooke Aug 13 '15 at 11:21
  • 6
    Not sure how this answer got upvoted so far when the original poster wrote: "Is there some way to get the status code when there is no exceptions thrown?" I guess no point in downvoting now. – Frog Pr1nce Dec 01 '15 at 17:18
36

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;
}
Dmitry S.
  • 8,373
  • 2
  • 39
  • 49
30

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;
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
24

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.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
kbrimington
  • 25,142
  • 5
  • 62
  • 74
  • 2
    it seems that the only way would be webrequest/response – julio Aug 26 '10 at 16:01
  • 1
    Seems a problem if you explicitly are looking for some other 200 series message (i.e. 201 CREATED - See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html). :-/ It would be nice if that was explicitly available even if the "intermediate" ones were skipped. – Norman H Nov 16 '11 at 17:24
  • 1
    @NormanH, I do not disagree. It would seem that WebClient is a bit of a leaky abstraction when it comes to status codes. Cheers! – kbrimington Nov 16 '11 at 18:28
9

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.

vdann
  • 103
  • 3
Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
4

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
            }
DFTR
  • 861
  • 10
  • 30
  • This worked great for me as i was looking for the response code. Nice solution! – evilfish Aug 11 '17 at 07:31
  • Be aware that [unlike with HttpClient] 4xx and 5xx responses result in a WebException being thrown at the "response = base.GetWebResponse(request);" line. You can pull the status and response from the exception (if they exist). – mwardm Apr 16 '18 at 13:33
  • Yes. You still have to catch exceptions just like normal. However, if there is not an exception, this exposes what the OP wanted. – DFTR Jun 05 '18 at 00:38
3

You should use

if (e.Status == WebExceptionStatus.ProtocolError)
{
   HttpWebResponse response = (HttpWebResponse)ex.Response;             
   if (response.StatusCode == HttpStatusCode.NotFound)
      System.Diagnostics.Debug.WriteLine("Not found!");
}
LeMoussel
  • 5,290
  • 12
  • 69
  • 122
  • 4
    This was voted up why? The OP clearly states: `However if the form is submitted successfully and no exception is thrown...` – Kenneth K. Aug 13 '14 at 19:21
1

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()
jpe
  • 1,013
  • 6
  • 14
-1

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;
}
Sergey
  • 1,552
  • 20
  • 18
-1

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

Paul Hadfield
  • 6,088
  • 2
  • 35
  • 56