refit icon indicating copy to clipboard operation
refit copied to clipboard

feature: Ability to catch all exceptions, throw no exception and return a result

Open andreinitescu opened this issue 3 years ago • 13 comments

There are already several issues (now all closed unfortunately) related to Refit not being able to catch exceptions which are not a result of server response or due to the serialization or deserialization:

https://github.com/reactiveui/refit/issues/719 https://github.com/reactiveui/refit/issues/273

Here's an example of exceptions not currently caught by Refit:

  • System.Net.Http.HttpRequestException (for example, when host is not reachable for example)
  • System.Net.WebException (for example, Error: ConnectFailure (Connection refused))
  • Exception due to cancelling the operation when CancellationToken was provided

These exceptions are NOT caught by the newer ExceptionFactory mechanism: https://github.com/reactiveui/refit#providing-a-custom-exceptionfactory

In a real app, as a developer, you cannot just ignore/swallow these exceptions. Many times you need to actually even show a message to the user so the user is aware. Therefore, instead of having to catch and handle these errors correctly in every app, I think it would be really very useful if correctly catching and handling all the exceptions is implemented right in the Refit library.

From an API change point of view, I'm not sure exactly how is the best to do this. @clairernovotny suggested here to have a new ApiRequestException which will ensure the new way will be completely backward compatible and doesn't break any of the existing apps. Which sounds good.

andreinitescu avatar Sep 10 '21 08:09 andreinitescu

Hi,

I found a possible issue in the class TaskToObservable, if you create a Task which will throw an exception and you dispose the subscription before the error is thrown AND if a garbage collection is done, than you'll get an UnobservedTaskException.

I made an WPF application to reproduce this problem, If you run the application and click on the button 'Click me' you'll see an exception in red, if you click on the 'switch button', and test again you'll see that the problem is fixed.

WpfApplication1.zip

vsab74 avatar Oct 29 '21 10:10 vsab74

I have the same issue

chrsoulier avatar Nov 03 '21 08:11 chrsoulier

Same issue too: any news on this subject please?

sblanc74 avatar Nov 03 '21 08:11 sblanc74

I have the same issue too

parisq avatar Nov 03 '21 08:11 parisq

same issue

gjaba avatar Nov 03 '21 08:11 gjaba

Exactly same pb for me

syboulayte avatar Nov 03 '21 08:11 syboulayte

Sorry @clairernovotny, can we have some feedback on this issue please ?

vsab74 avatar Nov 22 '21 14:11 vsab74

I have the same issue

PaGrom avatar Dec 15 '22 14:12 PaGrom

I have the same issue. At some point of my code, I get entities from 3 different sources: one is directly on my database (ok) and the other two are by APIs. If getting those entities from the APIs doesn't work as expected (for example "connection refused"), I can't just show an error to my client, I must just show some alerts. I made it work using try catch, but it seems to be kind of "Go Horse", would be awesome if we could kind of "map exceptions to httpResponses" on Refit.

ygorats avatar Feb 09 '23 20:02 ygorats

I am also having this issue on Android + iOS Xamarin.MAUI apps. If we use refit, the above exceptions are triggered when there is no connection. We can work around this, but it's annoying when it could be handled by refit.

JonnySKK avatar Mar 08 '23 15:03 JonnySKK

Hello,

I would like to give my solution to solve this issue.

  1. Create a new class called HttpRequestExceptionHandler and inherit from the HttpClientHandler class.
  2. Override the SendAsync and Send methods of HttpClientHandler . The code is as follows.
  3. HttpRequestExceptionHandler as a parameter of the constructor of the HttpClient.
  4. Use HttpClient as parameter of For of RestService.

Putting things together

  1. HttpRequestExceptionHandler.cs

public class HttpRequestExceptionHandler : HttpClientHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        try
        {
            return await base.SendAsync(request, cancellationToken);
        }
        catch (HttpRequestException exception)
        {
            // You can use other status codes, such as HttpStatusCode.GatewayTimeout etc.
            return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable)
            {
                Content = new StringContent(exception.Message),
                RequestMessage = request,
            };
        }
    }

    protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        try
        {
            return base.Send(request, cancellationToken);
        }
        catch (HttpRequestException exception)
        {
            return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable)
            {
                Content = new StringContent(exception.Message),
                RequestMessage = request,
            };
        }
    }
}
  1. App.cs
public partial class Program
{
    private static readonly HttpClient _http;

    static Program()
    {
        var handler = new HttpRequestExceptionHandler();
        _http = new HttpClient(handler) { BaseAddress = new Uri("https://api.github.com") };
    }

    private static async Task Main()
    {
        var gitHubApi = RestService.For<IGitHubApi>(_http);
        var octocat = await gitHubApi.GetUser("octocat");
    }
}

xixixixixiao avatar Mar 18 '23 18:03 xixixixixiao

Same issue. Any news?

AntonioFalcaoJr avatar Dec 21 '23 22:12 AntonioFalcaoJr