112

I'm probably missing something obvious here.

I'm using HttpClient which throws HttpRequestException that contains StatusCode in the Message string.

How can I access that StatusCode?


Edit: More info, I wrote this question in rush.

I'm using HttpClient to access another API within my WebApi project. Yes, I know why I'm calling EnsureSuccessStatusCode(). I want to propagate some errors downstream such as 404 and 403.

All I wanted was to consistently transform HttpRequestException into HttpResponseException using custom ExceptionFilterAttribute.

Unfortunately, HttpRequestException does not carry any extra info I could use besides the message. I was hoping to uncover StatusCode in raw (int or enum) form.

Looks like I can either:

  1. Use the message to switch the status code (bleh)
  2. Or create my version of EnsureSuccessStatusCode and throw exception that's actually usable.
Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
Kugel
  • 19,354
  • 16
  • 71
  • 103

8 Answers8

47

Status code was passed as part of a string to HttpRequestException so that you cannot recover it from such exceptions alone.

The design of System.Net.Http requires you to access HttpResponseMessage.StatusCode instead of waiting for the exception.

http://msdn.microsoft.com/en-us/library/system.net.http.httpresponsemessage(v=vs.110).aspx

If you are now following the Microsoft guide, make sure you understand clearly why it asks you to call HttpResponseMessage.EnsureSucessStatusCode. If you don't call that function, there should be no exception.

Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
Lex Li
  • 60,503
  • 9
  • 116
  • 147
  • 3
    I'm trying to address a cross cutting concern, here. Look at my edit, maybe there's a better solution. – Kugel Mar 06 '14 at 12:40
  • 2
    Does this fit with the use of HttpClient.GetStreamAsync(url) as I can't see a way of performing this action without having to strip out the message text? – The Senator Sep 08 '16 at 15:59
  • 1
    @TheSenator You have to call GetAsync(uri) or PostAsync(uri) to get back a HttpResponseMessage. If you try and get at the content of the response either by reading it or using a convenience method like GetStreamAsync, EnsureSuccessStatusCode is called under the hood. – odyth Mar 09 '18 at 02:21
  • 12
    Which begs the obvious question of what is the point of the convenience methods being available at all if they throw exceptions that don't make it possible to usefully handle the error. Littering your code with if and throw statements is no solution, so from what has been said here it appears the correct answer currently is that you have to reimplement either the convenience methods or EnsureSuccessStatusCode yourself. – Neutrino Nov 13 '18 at 14:28
  • 3
    That's easier said than done. The exception can be thrown from a 3rd party code. For example autogenerated swagger client. – user2555515 Aug 12 '20 at 21:35
32

For what its worth, this guy did something clever: https://social.msdn.microsoft.com/Forums/vstudio/en-US/dc9bc426-1654-4319-a7fb-383f00b68def/c-httpresponsemessage-throws-exception-httprequestexception-webexception-the-remote-name?forum=csharpgeneral

In the case where I needed an exception status property, I can do this:

catch (HttpRequestException requestException)
{
    if (requestException.InnerException is WebException webException && webException.Status == WebExceptionStatus.NameResolutionFailure)
    {
        return true;
    }

    return false;
}
ABS
  • 2,626
  • 3
  • 28
  • 44
Steve
  • 976
  • 12
  • 12
  • 16
    up-voted because sometimes you don't have access to the response, like when you'r forced to use a library that wraps the functionality and throws any exceptions. – Kell Jan 04 '17 at 10:56
  • 7
    the .net core 2.1 HttpClient has GetAsync() and GetStreamAsync(). The first will return a response while the other will call EnsureSucessStatusCode internally and throw an HttpRequestException. I don't understand the inconsistency and it makes handling errors difficult, but I appreciate this workaround. – smurtagh Jan 24 '19 at 13:43
  • 10
    Running .NET Core 3.1.5, `requestException.InnerException` is null so this won't work – Ohad Schneider Jul 07 '20 at 16:15
27

From .NET 5.0, HttpRequestException has a StatusCode property which will have a value if the exception represents a non-successful result, otherwise null. So you can use as follows:

try
{
   // Your code
}
catch (HttpRequestException httpRequestException)
{
   if ((int)httpRequestException.StatusCode == 401)
   {
        // Show unauthorized error message
   }
   else 
   {
       // Other error message
   }
}

For more details are here

Troy
  • 690
  • 1
  • 7
  • 25
TanvirArjel
  • 30,049
  • 14
  • 78
  • 114
3

As mentioned by others as well it's not a good practice to get the StatusCode from HttpRequestException, the same can be done beforehand with HttpResponseMessage.StatusCode after checking HttpResponseMessage.IsSuccessStatusCode

Anyhow if due to some constraint/requirement one has to read StatusCode, There can be two solution

  1. Extended the HttpResponseMessage with your custom exception explained here
  2. Hack on the HttpRequestException.ToString to get the StatusCode, As the message is a constant post fixed by StatusCode and Repharse.

Below is the code in System.Net.Http.HttpResponseMessage Where SR.net_http_message_not_success_statuscode ="Response status code does not indicate success: {0} ({1})."

public HttpResponseMessage EnsureSuccessStatusCode()
    {
        if (!this.IsSuccessStatusCode)
        {
            if (this.content != null)
            {
                this.content.Dispose();
            }
            throw new HttpRequestException(string.Format(CultureInfo.InvariantCulture, SR.net_http_message_not_success_statuscode, new object[]
            {
                (int)this.statusCode,
                this.ReasonPhrase
            }));
        }
        return this;
    }
Community
  • 1
  • 1
1

This has worked for me

var response = ex.Response;
var property = response.GetType().GetProperty("StatusCode");
if ( property != null && (HttpStatusCode)property.GetValue(response) == HttpStatusCode.InternalServerError)
1

in .Net 6

    try
    {
        return await _httpClient.GetFromJsonAsync<YourType>("<url>", cancellationToken);
    }
    catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.NotFound)   
    {
        // handle 404
    }
ravi
  • 949
  • 11
  • 22
0

For .Net Core 3.1 I could not make use of TanvirArjel or Lex Li's solution. So I went with manually calling the HttpClient's GetHttpAsync method and interrogating the Http status code myself. If the status code returns "OK" I process the HttpClient's Content property for the returned Html.

public async Task<string> GetHtml(string url)
{
    int retry = 3;

    while (retry > 0)
    {
        retry = retry - 1;

        try
        {   
            var result = await client.GetAsync(url);
            if (result.StatusCode != HttpStatusCode.OK)
            {
                switch (result.StatusCode)
                {
                    case HttpStatusCode.BadGateway:
                    case HttpStatusCode.BadRequest:
                    case HttpStatusCode.Forbidden:
                    case HttpStatusCode.ServiceUnavailable:
                    case HttpStatusCode.GatewayTimeout:
                        {
                            Global.Report($"CustomHttpClient: Temporary issue detected. Pausing to allow time to resolve.");
                            // Wait for temporary issue to resolve
                            await Task.Delay(120000);
                            continue;
                        }
                    default:
                        {
                            throw new Exception($"CustomHttpClient: Error {result.StatusCode}.");
                        }
                }
            }
            string response = await result.Content.ReadAsStringAsync();
            return response;
        }
        catch (Exception ex)
        {
            throw new Exception($"CustomHttpClient: Error downloading page => {url}. " + ex);
        }
    }

    throw new Exception($"CustomHttpClient: Temporary issue persists. Retries exhausted. Unable to download page => {url}.");
}
TheLegendaryCopyCoder
  • 1,658
  • 3
  • 25
  • 46
0

If what you need is to extract status code from HttpRequestException (eg: in a application exception handler), look at answers from others.

Otherwise you might take a look at HttpClient.GetAsync() method. Unlike methods like HttpClient.GetStringAsync() (which return simple data type and thrown and exception if something goes wrong), that one return you a HttpResponseMessage object everytime, regardless request success or failure.

From there you can check status code :

var response = await client.GetAsync(...);
if (!response.IsSuccessStatusCode)
{
    var statusCode = response.StatusCode;
    //throw an exception or something similar
}

var responseText = await response.Content.ReadAsStringAsync();
tigrou
  • 4,236
  • 5
  • 33
  • 59