I'm currently using Polly and Flurl together, but I have a common retry policy that I have to add to every request. I notice that Polly allows you to set a default using AddPolicyHandler(...) but this requires an IHttpClientBuilder and I can't see any way of getting hold of this from Flurl.
I thought overloading DefaultHttpClientFactory might be the way to go, but that only gives me access to the HttpClient, not the IHttpClientBuilder.
I know I could make my own HttpClients and pass them into Flurl, but I'd rather avoid that if I can as I'd like Flurl to manage their lifecycle.
Is there currently a way of doing what I want to do?
Great question. Flurl gives you all the necessary hooks to do this. First define a DelegatingHandler
that takes a Polly policy:
public class PollyHandler : DelegatingHandler
{
private readonly IAsyncPolicy<HttpResponseMessage> _policy;
public PollyHandler(IAsyncPolicy<HttpResponseMessage> policy) {
_policy = policy;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
return _policy.ExecuteAsync(ct => base.SendAsync(request, ct), cancellationToken);
}
}
Then create a custom IHttpClientFactory
that returns your custom handler with the default handler as its InnerHandler
:
public class PollyFactory : DefaultHttpClientFactory
{
private readonly IAsyncPolicy<HttpResponseMessage> _policy;
public PollyFactory(IAsyncPolicy<HttpResponseMessage> policy) {
_policy = policy;
}
public override HttpMessageHandler CreateMessageHandler() {
return new PollyHandler(_policy) {
InnerHandler = base.CreateMessageHandler()
};
}
}
Finally, on app startup, define your policy and register it with Flurl:
var policy = Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.RetryAsync(5);
FlurlHttp.Configure(settings => settings.HttpClientFactory = new PollyFactory(policy));
One important note is that this approach will not work with a policy that handles FlurlHttpException. That's because you're intercepting calls at the HttpMessageHandler
level here. Flurl converts responses and errors to FlurlHttpException
s higher up the stack, so those won't get trapped/retried with this approach. The policy in the example above traps HttpRequestException
and HttpResponseMessage
(with non-2XX status codes), which will work.