I use the HttpClientFactory together with Refit to make API calls. I want to use Polly for Retry, Circuit Breaker and Fallback, and while it's easy to set up Retry and Circuit Breaker, I struggle with Fallback, as it can only return a fallback value of type HttpResponseMessage, but what is being returned is a Refit.ApiResponse. I tried returning a dummy HttpResponseMessage, but then Refit breaks as it expects certain things to be present, e.g. the HttpRequestMessage.
Has anyone successfully returned a custom object as a fallback value when using HttpClientFactory with Refit?
This is the initial setup with Retry:
PolicyBuilder<HttpResponseMessage>? policyBuilder = Policy<HttpResponseMessage>
.Handle<Exception>();
IEnumerable<TimeSpan>? retryDelays = Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(1), retryCount: 5, fastFirst:true);
AsyncRetryPolicy<HttpResponseMessage>? retryPolicy = policyBuilder.WaitAndRetryAsync(retryDelays, (exception, timeSpan, retryCount, context) =>
{
Debug.WriteLine($"Exception occurred while called Account Service. Retry policy in effect | Retry Attempt: {retryCount} | WaitSeconds: {timeSpan.TotalSeconds}. Exception: {exception.Exception.Message}");
});
This is the circuit breaker:
AsyncCircuitBreakerPolicy<HttpResponseMessage>? circuitBreakerPolicy = policyBuilder
.CircuitBreakerAsync(breakCircuitAfterErrors, TimeSpan.FromMinutes(keepCircuitBreakForMinutes),
(exception, timespan, context) =>
{
// OnBreak, i.e. when circuit state changes to open
Debug.WriteLine(
$"Account Service circuit breaker policy in effect | State changed to Open (blocked). It will remain open for {keepCircuitBreakForMinutes} minutes");
},
(context) =>
{
// OnReset, i.e. when circuit state changes to closed
Debug.WriteLine("Account Service circuit breaker policy in effect | State changed to Closed (normal).");
});
This is the fallback with HttpResponseMessage, which breaks Refit:
AsyncFallbackPolicy<HttpResponseMessage> fallbackPolicyForCircuitBreaker = Policy<HttpResponseMessage>
.Handle<BrokenCircuitException>()
.FallbackAsync((cancellationToken) =>
{
// In our case we return a null response.
Debug.WriteLine($"The Circuit is Open (blocked). A fallback null value is returned. Try again later.");
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
});
And here I add the policy handlers to the client:
builder.Services.AddHttpClient("AccountService")
.AddPolicyHandler(fallbackPolicyForCircuitBreaker)
.AddPolicyHandler(retryPolicy)
.AddPolicyHandler(circuitBreakerPolicy);
FallbackAsynchas several overloads. One of them looks like this:This means in case of
fallbackActionyou can access the faultedHttpResponseMessagevia yourDelegateResult.But please bear in mind that
dr.Resultis only present if there was no exception. Otherwise.Resultwill benulland.Exceptionwill contain the thrown exception.If you need to access the request object then you can do that via closure. The
AddPolicyHandlerhas the following overloads:So, during your registration you can do the following: