Search code examples
c#parallel-processingpolly

Polly handle response and check status code


I creating a list of Tasks and executing them with Task.WhenAll(). If an http request returns error status I want to retry the call.

I'm getting and error on this line: ExecuteAsync(() => func()));

Cannot implicitly convert type 'System.Threading.Tasks.Task' to'System.Threading.Tasks.Task<System.Net.HttpWebResponse>'

I know what the error means, but the part I need help with is the overall implementation of Polly with parallel http calls and checking the response status.

Code:

var tasks = new List<Task<string>>();

var policy = Policy
                .Handle<HttpRequestException>()
                .OrResult<HttpWebResponse>(a => a.StatusCode != HttpStatusCode.OK)
                .WaitAndRetryForeverAsync(i => pauseBetweenFailures);

var urls = new List<string>();

foreach (var mediaItem in UploadedMediaItems)
{
    var mediaRequest = new HttpRequestMessage
    {
        RequestUri = new Uri("****"),
        Method = HttpMethod.Get,
        Headers = {
                { "id-token", id_Token },
                { "access-token", access_Token }
            }
    };

    async Task<string> func()
    {
        var response = await client.SendAsync(mediaRequest);
        return await response.Content.ReadAsStringAsync();
    }
    tasks.Add(policy.ExecuteAsync(() => func()));
}

await Task.WhenAll(tasks);

foreach (var t in tasks)
{
    var postResponse = await t;
    urls.Add(postResponse);
}

Solution

  • Disregarding any other issues (conceptual or otherwise)

    You have the wrong generic parameter HttpWebResponse, it should be HttpResponseMessage as that is what SendAsync returns

    var policy = Policy
       .Handle<HttpRequestException>()
       .OrResult<HttpResponseMessage>(a => a.StatusCode != HttpStatusCode.OK)
       ...
    

    Also, seemingly you would want to apply the policy to the SendAsync method, not the local method that returns a Task<string>

    async Task<string> func()
    {
       var response = await policy.ExecuteAsync(() => client.SendAsync(mediaRequest));
       return await response.Content.ReadAsStringAsync();
    }
    tasks.Add(func());
    

    Also note, that you will need to recreate a new HttpRequestMessage for every retry. They cannot be reused.

    Which may look more like this

    Task<HttpResponseMessage> Send()
    {
       var mediaRequest = new HttpRequestMessage {... };
       return client.SendAsync(mediaRequest);
    }
    
    async Task<string> func()
    {
        var response = await policy.ExecuteAsync(Send);
        return await response.Content.ReadAsStringAsync();
    }
    tasks.Add(func());
    

    However, on saying all this, you would likely want to use the more modern approach of adding Polly to the IHttpClientFactory implementation when you register the service via AddPolicyHandler, which will in turn take care of the request