Search code examples
c#asp.net-core-webapidotnet-httpclientpollyretry-logic

How can i invoke X-HttpMessageHandlers when retrying requests?


In short: I want to execute X-HttpMessageHandlers when retrying a request.

Implementation: Currently, I have added an HttpClient, whose request gets handled by a Logging- and a PolicyHandler:

using Microsoft.Extensions.Http.Polly
builder.Services.AddHttpClient()
    .AddHttpMessageHandler<LoggingHandler>()
    .AddPolicyHandler(GetRetryPolicy());

Policy: To keep the policy very simple, let's use an example:

public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    // HandleTransientHttpError: Handles HttpRequestException, 
    // Http status codes >= 500 (server errors) 
    // and status code 408 (request timeout)

    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .RetryAsync(3); // Retry the request up to 3 times
}

In case you want to test out the retry policy, send requests to: https://httpbin.org/status/503.

The above link will return an unsuccessful HttpResponseMessage with a status code 502, aka Bad Gateway. This response type is a server error, so it will get handled by the policy.

Sources:


Solution

  • TL; DR: You just have to change the message handlers registration order

    from

    .AddHttpMessageHandler<LogHandler>()
    .AddPolicyHandler(retryPolicy);
    

    to

    .AddPolicyHandler(retryPolicy)
    .AddHttpMessageHandler<LogHandler>();
    

    UPDATE #1

    In order to better understand why DelegatingHandlers' registration order matter I will extend my original post.

    The AddPolicyHandler

    This extension method basically registers a PolicyHttpMessageHandler instance into the handlers pipeline. This class implements the DelegatingHandler abstract class in a way that it applies a policy to the outgoing request. That's why the policy's type should be IAsyncPolicy<HttpResponseMessage>.

    The important stuff here is that from the HttpClient perspective your LogHandler and this PolicyHttpMessageHandler are treated in the same way.

    AddHttpMessageHandler, AddPolicyHandler

    If you register the handlers into the HttpClient's pipeline like you did then the output will be:

    Loghandler
    Retry attempt 1
    Retry attempt 2
    

    Lets see what happens under the hood

    wrong order

    I've used mermaid.js to draw this diagram and I've used autonumbering to be able to correlate action and emitted output.

    2: Loghandler
    7: Retry attempt 1
    10: Retry attempt 2
    

    AddPolicyHandler,AddHttpMessageHandler

    This time lets switch the registration order. The output will be:

    Loghandler
    Retry attempt 1
    Loghandler
    Retry attempt 2
    Loghandler
    

    The actions sequence:

    good order

    and finally the correlated log:

    4: Loghandler
    8: Retry attempt 1
    10: Loghandler
    14: Retry attempt 2
    16: Loghandler
    

    For more details please check out this SO topic.

    Here you can find a dotnet fiddle to be able to play with it.