Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
andreinitescu opened this issue Sep 10, 2021 · 13 comments

Comments

@andreinitescu
Copy link

andreinitescu commented Sep 10, 2021

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:

#719
#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.

@vsab74
Copy link

vsab74 commented Oct 29, 2021

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

@chrsoulier
Copy link

chrsoulier commented Nov 3, 2021

I have the same issue

@sblanc74
Copy link

sblanc74 commented Nov 3, 2021

Same issue too: any news on this subject please?

@parisq
Copy link

parisq commented Nov 3, 2021

I have the same issue too

@gjaba
Copy link

gjaba commented Nov 3, 2021

same issue

@syboulayte
Copy link

Exactly same pb for me

@vsab74
Copy link

vsab74 commented Nov 22, 2021

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

@PaGrom
Copy link

PaGrom commented Dec 15, 2022

I have the same issue

@ygorats
Copy link

ygorats commented Feb 9, 2023

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.

@JonnySKK
Copy link

JonnySKK commented Mar 8, 2023

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.

@xixixixixiao
Copy link

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");
    }
}

@AntonioFalcaoJr
Copy link

Same issue. Any news?

@mohamed-firoz
Copy link

mohamed-firoz commented Jul 17, 2024

Hi Team,
I know this is quite old, I am also facing this issue.

I use
AspNetCore 8.0.0

Refit" Version="7.1.2"
Refit.HttpClientFactory" Version="7.1.2"
Refit.Newtonsoft.Json" Version="7.1.2"

For now I used a solution as given in code.
Need your thoughts and suggestions.

``

public class ApiExceptionHandlingMiddleware : DelegatingHandler
{
    private readonly ILogger<ApiExceptionHandlingMiddleware> _logger;

    public ApiExceptionHandlingMiddleware(ILogger<ApiExceptionHandlingMiddleware> logger)
    {
        _logger = logger;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        try
        {
            return await base.SendAsync(request, cancellationToken);
        }
        catch (Exception ex) when (ex is ApiException or WebException or HttpRequestException)
        {
            _logger.LogError(ex, "{Handler} - Request could not be completed due to Unhandled Exception",
                nameof(ApiExceptionHandlingMiddleware));

            return new HttpResponseMessage(HttpStatusCode.InternalServerError)
            {
                RequestMessage = request, Content = new StringContent($"An error occurred: {ex.Message}")
            };
        }
    }
}

``

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests