using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Polly; using Polly.Wrap; using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http { /// /// HttpClient wrapper that integrates Retry and Circuit /// breaker policies when invoking HTTP services. /// Based on Polly library: https://github.com/App-vNext/Polly /// public class ResilientHttpClient : IHttpClient { private HttpClient _client; private PolicyWrap _policyWrapper; private ILogger _logger; public HttpClient Inst => _client; public ResilientHttpClient(Policy[] policies, ILogger logger) { _client = new HttpClient(); _logger = logger; // Add Policies to be applied _policyWrapper = Policy.WrapAsync(policies); } public Task GetStringAsync(string uri) => HttpInvoker(() => _client.GetStringAsync(uri)); public Task PostAsync(string uri, T item) => // a new StringContent must be created for each retry // as it is disposed after each call HttpInvoker(() => { var response = _client.PostAsync(uri, new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json")); // raise exception if HttpResponseCode 500 // needed for circuit breaker to track fails if (response.Result.StatusCode == HttpStatusCode.InternalServerError) { throw new HttpRequestException(); } return response; }); public Task DeleteAsync(string uri) => HttpInvoker(() => _client.DeleteAsync(uri)); private Task HttpInvoker(Func> action) => // Executes the action applying all // the policies defined in the wrapper _policyWrapper.ExecuteAsync(() => action()); } }