refit: How to handle Failure while connecting to API

How to I handle can’t connect to the API exception.

Currently my app crashes due to the following exception not been handled.

{System.Net.WebException: Error: ConnectFailure (Connection refused) ---> System.Net.Sockets.SocketException: Connection refused
  at System.Net.Sockets.Socket.Connect (System.Net.EndPoint remoteEP) [0x000cb] in /Users/builder/data/lanes/3415/7db2aac3/source/mono/mcs/class/System/System.Net.Sockets/Socket.cs:1313 
  at System.Net.WebConnection.Connect (System.Net.HttpWebRequest request) [0x0019b] in /Users/builder/data/lanes/3415/7db2aac3/source/mono/mcs/class/System/System.Net/WebConnection.cs:195 
  --- End of inner exception stack trace ---
  at System.Net.HttpWebRequest.EndGetRequestStream (IAsyncResult asyncResult) [0x00043] in /Users/builder/data/lanes/3415/7db2aac3/source/mono/mcs/class/System/System.Net/HttpWebRequest.cs:882 
  at System.Threading.Tasks.TaskFactory`1[TResult].FromAsyncCoreLogic (IAsyncResult iar, System.Func`2 endFunction, System.Action`1 endAction, System.Threading.Tasks.Task`1 promise, Boolean requiresSynchronization) [0x00014] in /Users/builder/data/lanes/3415/7db2aac3/source/mono/external/referencesource/mscorlib/system/threading/Tasks/FutureFactory.cs:550 
--- End of stack trace from previous location where exception was thrown ---

This is my API Interface

    public interface IUserService
    {
        [Post("/Account/Authenticate")]
        Task<User> Authenticate([Body] User user);
    }

Then this is how I initialize and call the service

            var _userService = RestService.For<IUserService>("http://192.168.0.100:44314/api");
            user = await _userService.Authenticate(user);

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 21 (4 by maintainers)

Most upvoted comments

This has been on my radar for a while. We should probably split ApiException into ApiRequestException and ApiResponseException that derive from a common base. We’re not handling request exceptions (e.g. dns errors, etc) at all at the moment, which is why you get the raw WebException.

A workaround in the meantime (if using C#6) is:

catch(Exception ex) when (ex is ApiException || ex is WebException)
{
    // do whatever you need to
}

I’ve actually used something similar to deal with retries for transient errors (this was before I knew about Polly):

public class TransientExceptionHelper
{
    private static readonly ISet<HttpStatusCode> TransientErrorStatusCodes = new HashSet<HttpStatusCode>(new[]
    {
        HttpStatusCode.BadGateway,
        HttpStatusCode.GatewayTimeout,
        HttpStatusCode.InternalServerError,
        HttpStatusCode.ServiceUnavailable,
        HttpStatusCode.RequestTimeout
    });
    public static bool IsTransient(Exception exception)
    {
        var apiException = exception as ApiException;
        if (apiException != null)
        {
            return TransientErrorStatusCodes.Contains(apiException.StatusCode);
        }
        return exception is HttpRequestException || exception is OperationCanceledException;
    }
}

// Usage
catch (Exception transientEx) when (TransientExceptionHelper.IsTransient(transientEx))
{
    // Handle retries
}

Is there a way to not throw an exception ? I mean, this will dramatically kill performances. Besides, it’s not really an exception since we got a response. An exception would be the server not responding at all.

There are two different things going on though…semantically, they are different exceptions. The ApiException is for when the API itself is throwing and WebException is when the wire/server/network itself is the issue. I’m not sure it really makes sense to combine them as you’d typically want to take different actions based on those.

For WebException you may want to retry a few times. For an ApiException you’d likely simply fail.

I still don’t get it why this library is throwing ApiException when status code is not successful. I agree with @oooolivierrrr - you got actual response so why treat it as exception? I’m returning e.g. BadRequest respones from the api that are already wrapped into custom WebApiResponse that contains some extras that I may use on the client and if I want to not deal with exceptions in my case I would need to define my api client as: Task<ApiResponse<WebApiResponse<User>>> Authenticate([Body] User user); which is hilarious and wrapping client calls with try catch is out of option because this is not a valid solution and just a hacky way to make it actually work.