The library provides an outgoing request resiliency pipeline for HttpClient, using policies from the PoliNorError library.
- Provides the ability to create a resiliency pipeline to handle typical transient HTTP failures (including the
HttpRequestExceptionexception). - Flexible transient failure filter for the final
DelegatingHandlerin the pipeline for the response. - Additionally, custom failure status codes or categories can be added to the final handler filter.
- Other exception types (besides
HttpRequestException) can also be included in the final handler filter. - Inclusion in the outer handler filter of any
Exceptiontype thrown by the inner handler is also supported. - Both typed and named
HttpClient, as well asIHttpClientFactory, can be used. - Targets .NET Standard 2.0.
- π¦ Resiliency pipeline - the pipeline of
DelegatingHandler, using policies from thePoliNorErrorlibrary. - β‘ OuterHandler is the first handler in the pipeline (closest to the request initiator).
- β¬ InnerHandler is the next handler in the pipeline (closer to the final destination).
- π΅ FinalHandler is the innermost handler in the pipeline.
- β Transient HTTP errors are temporary failures that occur when making HTTP requests (HTTP 5xx, HTTP 408, HTTP 429 and
HttpRequestException).
β Configure typed or named HttpClient:
services.AddHttpClient<IAskCatService, AskCatService>((sp, config) =>
{
...
config.BaseAddress = new Uri(settings.BaseUri);
...
})..., where AskCatService is a service that implements IAskCatService, with HttpClient or IHttpClientFactory injected.
π§© Use the library's IHttpClientBuilder.WithResiliencePipeline extension method and configure the pipeline of DelegatingHandlers by using the AddPolicyHandler method with the policy you want to apply in this handler:
services.AddHttpClient<IAskCatService, AskCatService>((spForClient, client) =>
{
...
})
.WithResiliencePipeline((pb) =>
pb
.AddPolicyHandler(PolicyJustCreated)
.AddPolicyHandler((IServiceProvider sp) => funcThatUsesServiceProviderToCreatePolicy(sp))
...
)Or use the WithResiliencePipeline method overload that includes an additional context parameter:
services.AddHttpClient<IAskCatService, AskCatService>((spForClient, client) =>
{
...
})
.WithResiliencePipeline<SomeContextType>((pb) =>
pb
.AddPolicyHandler((SomeContextType ctx, IServiceProvider sp) =>
funcThatUsesContextAndServiceProviderToCreatePolicy(ctx, sp))
.AddPolicyHandler((IServiceProvider sp) => funcThatUsesServiceProviderToCreatePolicy(sp))
...
, context), where
pb- represents the pipeline builder.PolicyJustCreated- a policy from the PoliNorError library.funcThatUsesServiceProviderToCreatePolicy-Functhat uses theIServiceProviderto create a policy.funcThatUsesContextAndServiceProviderToCreatePolicy-Functhat uses theIServiceProviderand context to create a policy.
π΅ When you want to complete the pipeline, call the AsFinalHandler method for the last added handler and configure HttpErrorFilter to filter transient http errors and/or any non-successful status codes or categories:
services.AddHttpClient<IAskCatService, AskCatService>((sp, config) =>
{
...
})
.WithResiliencePipeline((pb) =>
pb
...
.AddPolicyHandler(PolicyForFinalHandler)
// β Adds transient http errors to the response handling filter.
.AsFinalHandler(HttpErrorFilter.HandleTransientHttpErrors())
...
)Additionally, you can include custom failure status codes or categories in the final handler filter:
...
.AsFinalHandler(HttpErrorFilter.HandleHttpRequestException()
// β Also adds 5XX status codes to the response handling filter.
.OrServerError())
...You can also include in the filter any exception type thrown by an inner handler:
...
.AsFinalHandler(HttpErrorFilter.HandleTransientHttpErrors())
// β Include 'SomeExceptionFromNonPipelineHandler' exceptions in the filter
//when thrown by a non-pipeline handler (in this case).
.IncludeException<SomeExceptionFromNonPipelineHandler>()
...βΎ In a service that uses HttpClient or HttpClientFactory, wrap the call to HttpClient in a catch block that handles the special HttpPolicyResultException exception.
If the request was not successful, examine the HttpPolicyResultException properties in this handler for details of the response:
try
{
...
using var response = await _client.GetAsync(uri, token);
...
}
catch (OperationCanceledException oe)
{
...
}
catch (HttpPolicyResultException hpre)
{
// β If the response status code matches the handling filter status code:
if (hpre.HasFailedResponse)
{
//For example, log a failed status code.
logger.LogError("Failed status code: {StatusCode}.", hpre.FailedResponseData.StatusCode);
}
}
catch (Exception ex)
{
...
}TheΒ AddRetryHandlerΒ extension methods provide a fluent way to attach a RetryPolicy to an HTTP message handler pipeline.
One of these methods allows adding a handler via RetryPolicyOptions and is responsible for setting up RetryPolicy details, including:
- Error processing,
- Policy result handling,
- Error filters,
- Policy naming,
- Delay between retries,
- And ultimately registering the policy with
AddPolicyHandler.
For example:
var retryOptions = new RetryPolicyOptions()
{
PolicyName = "MyRetryPolicy",
ConfigureErrorProcessing = (bp) =>
bp.WithErrorProcessorOf(
(Exception ex, ProcessingErrorInfo pi) =>
loggerTest.LogError(
ex,
"Exception on attempt { Attempt }:",
pi.GetRetryCount() + 1)),
ConfigureErrorFilter = (f) => f.ExcludeError<SomeException>(),
ConfigurePolicyResultHandling = (handlers) => handlers.AddHandler(
(pr, _) =>
{
if (pr.IsFailed)
{
loggerTest.LogWarning(
"{Errors} exceptions were thrown during handling by {PolicyName}.",
pr.Errors.Count(),
pr.PolicyName);
}
}
),
RetryDelay = ConstantRetryDelay.Create(TimeSpan.FromSeconds(1))
};This example configures RetryPolicyOptions with:
- A policy name ("MyRetryPolicy"),
- An error processor (logs exceptions with attempt numbers),
- An error filter (excludes
SomeException), - A result handler (logs warnings about exception counts),
- A 1-second constant delay between retries.
This is how RetryPolicyOptions is passed to AddRetryHandler in the pipeline:
services.AddHttpClient<IAskCatService, AskCatService>((sp, config) =>
{
...
})
.WithResiliencePipeline((pb) =>
pb
...
//Maximum number of retries: 3
.AddRetryHandler(3, retryOptions)
.AsFinalHandler(HttpErrorFilter.HandleTransientHttpErrors())
...
)You can also configure RetryPolicy details inline using the AddRetryHandler overload that accepts an Action<RetryPolicyOptions>.
Public properties of the HttpPolicyResultException:
InnerException- If the response status code matches the handling filterβs status code, it will be a special
FailedHttpResponseException. - If no handlers inside or outside the resiliency pipeline throw an exception, and the
HttpClientβs primary handler throws anHttpRequestException, theInnerExceptionwill be thatHttpRequestException. - Otherwise, the exception originates from one of the handlers, either inside or outside the pipeline.
- If the response status code matches the handling filterβs status code, it will be a special
FailedResponseData- not null if the status code part of the handling filter matches the response status code.HasFailedResponse- true ifFailedResponseDatais not null.PolicyResult- specifies thePolicyResult<HttpResponseMessage>result that is produced by a policy that belongs to theDelegatingHandlerthat throws this exception.InnermostPolicyResult- specifies thePolicyResult<HttpResponseMessage>result produced by a policy of the final handler or by a handler in the pipeline that throws its own exception.IsErrorExpected- indicates whether the filter for the original exception was satisfied.IsCanceled- indicates whether the execution was canceled.
See the /samples folder for concrete examples.
Steve Gordon. HttpClientFactory in ASP.NET Core 2.1 (Part 3) :
https://www.stevejgordon.co.uk/httpclientfactory-aspnetcore-outgoing-request-middleware-pipeline-delegatinghandlers
Martin Tomka. Building resilient cloud services with .NET 8 : https://devblogs.microsoft.com/dotnet/building-resilient-cloud-services-with-dotnet-8/
Thomas Levesque. Fun with the HttpClient pipeline : https://thomaslevesque.com/2016/12/08/fun-with-the-httpclient-pipeline/
Milan Jovanovic. Extending HttpClient With Delegating Handlers in ASP.NET Core :
https://www.milanjovanovic.tech/blog/extending-httpclient-with-delegating-handlers-in-aspnetcore
Josef Ottosson. Testing your Polly policies :
https://josef.codes/testing-your-polly-policies/

