Search code examples
c#dotnet-httpclientpolly

How to check status of response from Polly?


I am downloading a .tgz file (max 100 MB) in a particular folder from a url with the below code and it works fine. I am using HttpClient along with Polly for timeout and retries.

private static HttpClient _httpClient = new HttpClient()
{
    Timeout = TimeSpan.FromSeconds(5)
};

private async Task<bool> Download(string fileUrl)
{
    var configs = await Policy
       .Handle<TaskCanceledException>()
       .WaitAndRetryAsync(retryCount: 3, sleepDurationProvider: i => TimeSpan.FromMilliseconds(300))
       .ExecuteAsync(async () =>
       {
           using (var httpResponse = await _httpClient.GetAsync(fileUrl).ConfigureAwait(false))
           {
               httpResponse.EnsureSuccessStatusCode();
               return await httpResponse.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
           }
       }).ConfigureAwait(false);

    File.WriteAllBytes("Testing/data.tgz", configs);
    return true;
}

As you can see in the above method, I am always returning true but it is not correct. I wanted to do below things:

  • If for some reason I am not able to connect to the URL after x retries, then I want to return false and log it saying cannot talk to the url.
  • Also if I am not able to create a file or write the byte array in a file on the disk then I want to return false as well. I am not sure when will this case happen but I want to protect us from this. Is this possible to achieve in my example?

For question one - I was reading more about Polly but couldn't find on how to check the response of Polly call after x number of retries and then act accordingly. And for question two - I am not sure on how we can do that. Also since I recently started working with C# (like few weeks maybe), I could be doing something wrong in my above code so if there is any better way, let me know.


Solution

  • If I understand your question there is a section on the page denoted to capturing the response

    Post-execution: capturing the result, or any final exception

    var policyResult = await Policy
                  .Handle<HttpRequestException>()
                  .RetryAsync()
                  .ExecuteAndCaptureAsync(() => DoSomethingAsync());
    /*              
    policyResult.Outcome - whether the call succeeded or failed         
    policyResult.FinalException - the final exception captured, will be null if the call succeeded
    policyResult.ExceptionType - was the final exception an exception the policy was defined to handle (like HttpRequestException above) or an unhandled one (say Exception). Will be null if the call succeeded.
    policyResult.Result - if executing a func, the result if the call succeeded or the type's default value
    */
    

    Update

    var policyResult = await Policy
       .Handle<TaskCanceledException>()
       .WaitAndRetryAsync(retryCount: 3, sleepDurationProvider: i => TimeSpan.FromMilliseconds(300))
       .ExecuteAndCaptureAsync(async () =>
       {
          using (var httpResponse = await _httpClient.GetAsync("Something").ConfigureAwait(false))
          {
             httpResponse.EnsureSuccessStatusCode();
             return await httpResponse.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
          }
       }).ConfigureAwait(false);
    
    if (policyResult.Outcome == OutcomeType.Failure)
       return false;
    
    try
    {
    
        File.WriteAllBytes("Testing/data.tgz", policyResult.Result);      
        return true;
    }
    catch(Exception ex)
    {  
        // usually you wouldn't want to catch ALL exceptions (in general), however this is updated from the comments in the chat
        // file operations can fail for lots of reasons, maybe best catch and log the results. However ill leave these details up to you
        // log the results
        return false
    }