Fault Handling & Integration With Polly - MelbourneDeveloper/RestClient.Net GitHub Wiki
Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.
The Client
class uses the Microsoft class HttpClient to make HTTP calls. The CreateHttpClient
delegate allows for the construction of HttpClients to be handled by other libraries and frameworks. Polly integrates with Microsoft's dependency injection system and therefore Microsoft's Http Client Factory can be used in conjunction with RestClient.Net and Polly to achieve rich, policy-based fault handling. See the section on DI.
Here is an example that would normally appear in ASP.NET Core style usage but can be used on any platform where the Microsoft IoC container is used. Please see this article for more context.
//Configure a Polly policy
var policy = HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
//Create a Microsoft IoC Container
var serviceCollection = new ServiceCollection();
var baseUri = new Uri("https://restcountries.eu/rest/v2/");
serviceCollection.AddSingleton(typeof(ISerializationAdapter), typeof(NewtonsoftSerializationAdapter));
serviceCollection.AddSingleton(typeof(ILogger), typeof(ConsoleLogger));
//Add the Polly policy to the named HttpClient instance
serviceCollection.AddHttpClient("rc", (c) => { c.BaseAddress = baseUri; }).
SetHandlerLifetime(TimeSpan.FromMinutes(5)).
AddPolicyHandler(policy);
//Provides mapping for Microsoft's IHttpClientFactory (This is what makes the magic happen)
serviceCollection.AddDependencyInjectionMapping();
var serviceProvider = serviceCollection.BuildServiceProvider();
var clientFactory = serviceProvider.GetService<CreateClient>();
//Create a Rest Client that will get the HttpClient by the name of rc
var client = clientFactory("rc");
//Make the call
var response = await client.GetAsync<List<RestCountry>>();
Policies can also be applied manually like so. this code uses Polly to handle transient errors, but it's also possible to manually handle retries without Polly.
var tries = 0;
var policy = HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(response => response.StatusCode == HttpStatusCode.NotFound)
.RetryAsync(3);
var client = new Client(
new ProtobufSerializationAdapter(),
null,
new Uri(UnitTests.LocalBaseUriString),
logger: null,
createHttpClient: UnitTests.GetTestClientFactory().CreateClient,
sendHttpRequestFunc: (httpClient, httpRequestMessageFunc, logger, cancellationToken) =>
{
return policy.ExecuteAsync(() =>
{
var httpRequestMessage = httpRequestMessageFunc.Invoke();
//On the third try change the Url to a the correct one
if (tries == 2) httpRequestMessage.RequestUri = new Uri("Person", UriKind.Relative);
tries++;
return httpClient.SendAsync(httpRequestMessage, cancellationToken);
});
});
var person = new Person { FirstName = "Bob", Surname = "Smith" };
//Note the Uri here is deliberately incorrect. It will cause a 404 Not found response. This is to make sure that polly is working
person = await client.PostAsync<Person, Person>(person, new Uri("person2", UriKind.Relative));
Assert.AreEqual("Bob", person.FirstName);
Assert.AreEqual(3, tries);