6

Whenever I use the Windows.Web.Http.HttpClient class to make HTTP requests, I always handle network exceptions like this:

HttpResponseMessage response;

try
{
    response = await httpClent.GetAsync(new Uri("http://www.microsoft.com"));
}
catch (Exception e)
{
    // Most likely a network exception.
    // Inspect e.HResult value to see what the specific error was.
}

But now I'll catch all exceptions instead of just network exceptions, especially if the try block encompasses more than just the httpClient.GetAsync call.

Various exception HRESULTs are already converted into appropriate managed types automatically at the ABI layer (e.g. E_OUTOFMEMORY is projected to System.OutOfMemoryException), so why are network exceptions not projected in a similar manner?

Decade Moon
  • 32,968
  • 8
  • 81
  • 101

3 Answers3

3

There are a very small number of exception types defined by WinRT, and a limited number of HRESULTs that will project specially into C#.

In general, the WinRT API design pattern avoids exceptions for everything except things that are programming errors and should be discovered at design-time (invalid arguments, missing capabilities, etc) or things that you can't really recover from (such as out-of-memory). You should avoid handling these types of exceptions with try \ catch because they represent bugs in your app or an inability of the system to keep running your app.

Instead, WinRT prefers to have methods succeed but return objects with status codes in them (eg, ResponseCode) that you can query to see whether the method completed successfully or not.

The reasoning for this is that many developers fail to handle exceptions (due to not fully testing their app under different configurations). An unhandled exception is guaranteed to bring down the process, which isn't a great experience for customers, but a return value that indicates failure can often be handled by apps, either because they were already checking the status for other reasons (eg, you probably always want to check the HTTP status, whether you got an error or not) or because the code is already resilient to "empty" results (eg, foreach over an empty list is well-defined).

Not all APIs follow this pattern - especially those designed early on in Windows 8 -- but it is a pattern you should see in most WinRT APIs. You will also notice a lot of Try-style APIs in WinRT that attempt to do something and return true or false rather than throwing an exception. So for the most part, your code should be free of try / catch blocks around WinRT API calls, although you might still need to use them for your own code or 3rd-party libraries.

Peter Torr - MSFT
  • 11,824
  • 3
  • 18
  • 51
  • 3
    How would you deal with the example in the Question? The only way seems to be catch the general Exception and inspect the HResult. – tagy22 Sep 01 '15 at 09:52
  • 2
    Yes, that is the only real way to handle the exceptions that don't have specific projections – Peter Torr - MSFT Sep 01 '15 at 16:50
  • I think this is a good answer but think it'd be better with a little extra guidance - as per tagy22's comment. E.g. thoughts on using response.IsSuccessStatusCode – Adrian K Sep 27 '17 at 20:47
3

I don't know why Windows.Web.Http.HttpClient class exceptions are not automatically wrapped in appropriate managed types, but (thankfully!) there is a method which allows to get the actual reason -- Windows.Web.WebError.GetStatus.

For example:

using (var client = new HttpClient())
{
    var request = new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.example.com"));

    try
    {
        // Http-errors are returned in the response, and no exception is thrown.
        HttpResponseMessage response = await client.SendRequestAsync(request);
    }
    catch (Exception ex)
    {
        WebErrorStatus error = WebError.GetStatus(ex.HResult);
        // For example, if your device could not connect to the internet at all,
        // the error would be WebErrorStatus.HostNameNotResolved.
    }
}
Yarik
  • 1,423
  • 1
  • 14
  • 16
0

For what it's worth, I have been struggling a bit to decide how to handle errors (particularly network related ones) when using Windows.Web.Http.HttpClient in UWP apps.

The pattern I settled on was to return an object (which I can return using a Task) that contains either the information or the exception:

private class MyResponseObject
{
  public string Data = string.Empty;
  // Alternatively you could return the HttpResponseMessage (I guess).
  //public HttpResponseMessage HttpResponseMessage;

  public Exception Exception = null;
}

And specifically, to make use of checking the responses IsSuccessStatusCode property immediately after getting the response back:

private async Task<MyResponseObject> CallService(Uri url)
{
    MyResponseObject r = new MyResponseObject();

    try
    {
        HttpResponseMessage response = await httpClient.GetAsync(url);

        if (response.IsSuccessStatusCode)
        {
            // do something with the information successfully received
            r.Data = await response.Content.ReadAsStringAsync();
        }
    }
    catch (Exception ex)
    {
        // do something with the exception
        r.Exception = ex;
    }

    return r;
}
Adrian K
  • 9,880
  • 3
  • 33
  • 59