Added HTTPClient services and review application on WebMVC
This commit is contained in:
parent
dc5de83747
commit
2f9fa4dcca
@ -36,7 +36,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
|
||||
var basket = await _repository.GetBasketAsync(id);
|
||||
if (basket == null)
|
||||
{
|
||||
return NotFound();
|
||||
return Ok(new CustomerBasket(id) { });
|
||||
}
|
||||
|
||||
return Ok(basket);
|
||||
|
@ -7,9 +7,8 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
||||
{
|
||||
public class AppSettings
|
||||
{
|
||||
public Connectionstrings ConnectionStrings { get; set; }
|
||||
//public Connectionstrings ConnectionStrings { get; set; }
|
||||
public string MarketingUrl { get; set; }
|
||||
|
||||
public string PurchaseUrl { get; set; }
|
||||
public string SignalrHubUrl { get; set; }
|
||||
public bool ActivateCampaignDetailFunction { get; set; }
|
||||
|
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WebMVC.Infrastructure
|
||||
{
|
||||
public class HttpClientRequestIdDelegatingHandler
|
||||
: DelegatingHandler
|
||||
{
|
||||
|
||||
public HttpClientRequestIdDelegatingHandler()
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put)
|
||||
{
|
||||
request.Headers.Add("x-requestid", Guid.NewGuid().ToString());
|
||||
}
|
||||
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,8 +3,8 @@ using System;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
|
||||
{
|
||||
public interface IResilientHttpClientFactory
|
||||
{
|
||||
ResilientHttpClient CreateResilientHttpClient();
|
||||
}
|
||||
//public interface IResilientHttpClientFactory
|
||||
//{
|
||||
// ResilientHttpClient CreateResilientHttpClient();
|
||||
//}
|
||||
}
|
@ -7,60 +7,60 @@ using System.Net.Http;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure
|
||||
{
|
||||
public class ResilientHttpClientFactory : IResilientHttpClientFactory
|
||||
{
|
||||
private readonly ILogger<ResilientHttpClient> _logger;
|
||||
private readonly int _retryCount;
|
||||
private readonly int _exceptionsAllowedBeforeBreaking;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
//public class ResilientHttpClientFactory : IResilientHttpClientFactory
|
||||
//{
|
||||
// private readonly ILogger<ResilientHttpClient> _logger;
|
||||
// private readonly int _retryCount;
|
||||
// private readonly int _exceptionsAllowedBeforeBreaking;
|
||||
// private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public ResilientHttpClientFactory(ILogger<ResilientHttpClient> logger, IHttpContextAccessor httpContextAccessor, int exceptionsAllowedBeforeBreaking = 5, int retryCount = 6)
|
||||
{
|
||||
_logger = logger;
|
||||
_exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
|
||||
_retryCount = retryCount;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
// public ResilientHttpClientFactory(ILogger<ResilientHttpClient> logger, IHttpContextAccessor httpContextAccessor, int exceptionsAllowedBeforeBreaking = 5, int retryCount = 6)
|
||||
// {
|
||||
// _logger = logger;
|
||||
// _exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
|
||||
// _retryCount = retryCount;
|
||||
// _httpContextAccessor = httpContextAccessor;
|
||||
// }
|
||||
|
||||
|
||||
public ResilientHttpClient CreateResilientHttpClient()
|
||||
=> new ResilientHttpClient((origin) => CreatePolicies(), _logger, _httpContextAccessor);
|
||||
// public ResilientHttpClient CreateResilientHttpClient()
|
||||
// => new ResilientHttpClient((origin) => CreatePolicies(), _logger, _httpContextAccessor);
|
||||
|
||||
private Policy[] CreatePolicies()
|
||||
=> new Policy[]
|
||||
{
|
||||
Policy.Handle<HttpRequestException>()
|
||||
.WaitAndRetryAsync(
|
||||
// number of retries
|
||||
_retryCount,
|
||||
// exponential backofff
|
||||
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
|
||||
// on retry
|
||||
(exception, timeSpan, retryCount, context) =>
|
||||
{
|
||||
var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " +
|
||||
$"of {context.PolicyKey} " +
|
||||
$"at {context.OperationKey}, " +
|
||||
$"due to: {exception}.";
|
||||
_logger.LogWarning(msg);
|
||||
_logger.LogDebug(msg);
|
||||
}),
|
||||
Policy.Handle<HttpRequestException>()
|
||||
.CircuitBreakerAsync(
|
||||
// number of exceptions before breaking circuit
|
||||
_exceptionsAllowedBeforeBreaking,
|
||||
// time circuit opened before retry
|
||||
TimeSpan.FromMinutes(1),
|
||||
(exception, duration) =>
|
||||
{
|
||||
// on circuit opened
|
||||
_logger.LogTrace("Circuit breaker opened");
|
||||
},
|
||||
() =>
|
||||
{
|
||||
// on circuit closed
|
||||
_logger.LogTrace("Circuit breaker reset");
|
||||
})
|
||||
};
|
||||
}
|
||||
// private Policy[] CreatePolicies()
|
||||
// => new Policy[]
|
||||
// {
|
||||
// Policy.Handle<HttpRequestException>()
|
||||
// .WaitAndRetryAsync(
|
||||
// // number of retries
|
||||
// _retryCount,
|
||||
// // exponential backofff
|
||||
// retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
|
||||
// // on retry
|
||||
// (exception, timeSpan, retryCount, context) =>
|
||||
// {
|
||||
// var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " +
|
||||
// $"of {context.PolicyKey} " +
|
||||
// $"at {context.OperationKey}, " +
|
||||
// $"due to: {exception}.";
|
||||
// _logger.LogWarning(msg);
|
||||
// _logger.LogDebug(msg);
|
||||
// }),
|
||||
// Policy.Handle<HttpRequestException>()
|
||||
// .CircuitBreakerAsync(
|
||||
// // number of exceptions before breaking circuit
|
||||
// _exceptionsAllowedBeforeBreaking,
|
||||
// // time circuit opened before retry
|
||||
// TimeSpan.FromMinutes(1),
|
||||
// (exception, duration) =>
|
||||
// {
|
||||
// // on circuit opened
|
||||
// _logger.LogTrace("Circuit breaker opened");
|
||||
// },
|
||||
// () =>
|
||||
// {
|
||||
// // on circuit closed
|
||||
// _logger.LogTrace("Circuit breaker reset");
|
||||
// })
|
||||
// };
|
||||
//}
|
||||
}
|
||||
|
@ -12,14 +12,14 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
{
|
||||
public class BasketService : IBasketService
|
||||
{
|
||||
private readonly IOptionsSnapshot<AppSettings> _settings;
|
||||
private readonly IOptions<AppSettings> _settings;
|
||||
private readonly HttpClient _apiClient;
|
||||
private readonly string _basketByPassUrl;
|
||||
private readonly string _purchaseUrl;
|
||||
|
||||
private readonly string _bffUrl;
|
||||
|
||||
public BasketService(HttpClient httpClient,IOptionsSnapshot<AppSettings> settings)
|
||||
public BasketService(HttpClient httpClient, IOptions<AppSettings> settings)
|
||||
{
|
||||
_apiClient = httpClient;
|
||||
_settings = settings;
|
||||
@ -30,21 +30,22 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
|
||||
public async Task<Basket> GetBasket(ApplicationUser user)
|
||||
{
|
||||
var getBasketUri = API.Basket.GetBasket(_basketByPassUrl, user.Id);
|
||||
var uri = API.Basket.GetBasket(_basketByPassUrl, user.Id);
|
||||
|
||||
var dataString = await _apiClient.GetStringAsync(getBasketUri);
|
||||
var responseString = await _apiClient.GetStringAsync(uri);
|
||||
|
||||
return string.IsNullOrEmpty(dataString) ?
|
||||
new Basket() { BuyerId = user.Id} :
|
||||
JsonConvert.DeserializeObject<Basket>(dataString);
|
||||
return string.IsNullOrEmpty(responseString) ?
|
||||
new Basket() { BuyerId = user.Id } :
|
||||
JsonConvert.DeserializeObject<Basket>(responseString);
|
||||
}
|
||||
|
||||
public async Task<Basket> UpdateBasket(Basket basket)
|
||||
{
|
||||
var updateBasketUri = API.Basket.UpdateBasket(_basketByPassUrl);
|
||||
var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
|
||||
var uri = API.Basket.UpdateBasket(_basketByPassUrl);
|
||||
|
||||
var response = await _apiClient.PostAsync(updateBasketUri, content);
|
||||
var basketContent = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _apiClient.PostAsync(uri, basketContent);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
@ -53,18 +54,18 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
|
||||
public async Task Checkout(BasketDTO basket)
|
||||
{
|
||||
var updateBasketUri = API.Basket.CheckoutBasket(_basketByPassUrl);
|
||||
var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
|
||||
var uri = API.Basket.CheckoutBasket(_basketByPassUrl);
|
||||
var basketContent = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _apiClient.PostAsync(updateBasketUri, content);
|
||||
var response = await _apiClient.PostAsync(uri, basketContent);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities)
|
||||
{
|
||||
var uri = API.Purchase.UpdateBasketItem(_purchaseUrl);
|
||||
|
||||
var updateBasketUri = API.Purchase.UpdateBasketItem(_purchaseUrl);
|
||||
var basketUpdate = new
|
||||
{
|
||||
BasketId = user.Id,
|
||||
@ -75,9 +76,9 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
}).ToArray()
|
||||
};
|
||||
|
||||
var content = new StringContent(JsonConvert.SerializeObject(basketUpdate), System.Text.Encoding.UTF8, "application/json");
|
||||
var basketContent = new StringContent(JsonConvert.SerializeObject(basketUpdate), System.Text.Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _apiClient.PutAsync(updateBasketUri,content);
|
||||
var response = await _apiClient.PutAsync(uri, basketContent);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
@ -88,17 +89,18 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
|
||||
public async Task<Order> GetOrderDraft(string basketId)
|
||||
{
|
||||
var draftOrderUri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId);
|
||||
var response = await _apiClient.GetStringAsync(draftOrderUri);
|
||||
var uri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId);
|
||||
|
||||
return JsonConvert.DeserializeObject<Order>(response);
|
||||
var responseString = await _apiClient.GetStringAsync(uri);
|
||||
|
||||
var response = JsonConvert.DeserializeObject<Order>(responseString);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task AddItemToBasket(ApplicationUser user, int productId)
|
||||
{
|
||||
var updateBasketUri = API.Purchase.AddItemToBasket(_purchaseUrl);
|
||||
var uri = API.Purchase.AddItemToBasket(_purchaseUrl);
|
||||
|
||||
var newItem = new
|
||||
{
|
||||
@ -107,9 +109,9 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
Quantity = 1
|
||||
};
|
||||
|
||||
var content = new StringContent(JsonConvert.SerializeObject(newItem), System.Text.Encoding.UTF8, "application/json");
|
||||
var basketContent = new StringContent(JsonConvert.SerializeObject(newItem), System.Text.Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _apiClient.PostAsync(updateBasketUri, content);
|
||||
var response = await _apiClient.PostAsync(uri, basketContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,69 +1,49 @@
|
||||
namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
{
|
||||
using global::WebMVC.Infrastructure;
|
||||
using AspNetCore.Authentication;
|
||||
using AspNetCore.Http;
|
||||
using BuildingBlocks.Resilience.Http;
|
||||
using ViewModels;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using ViewModels;
|
||||
|
||||
public class CampaignService : ICampaignService
|
||||
{
|
||||
private readonly IOptionsSnapshot<AppSettings> _settings;
|
||||
private readonly IHttpClient _apiClient;
|
||||
private readonly IOptions<AppSettings> _settings;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CampaignService> _logger;
|
||||
private readonly string _remoteServiceBaseUrl;
|
||||
private readonly IHttpContextAccessor _httpContextAccesor;
|
||||
|
||||
public CampaignService(IOptionsSnapshot<AppSettings> settings, IHttpClient httpClient,
|
||||
ILogger<CampaignService> logger, IHttpContextAccessor httpContextAccesor)
|
||||
public CampaignService(IOptions<AppSettings> settings, HttpClient httpClient, ILogger<CampaignService> logger)
|
||||
{
|
||||
_settings = settings;
|
||||
_apiClient = httpClient;
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
|
||||
_remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/m/campaigns/";
|
||||
_httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor));
|
||||
}
|
||||
|
||||
public async Task<Campaign> GetCampaigns(int pageSize, int pageIndex)
|
||||
{
|
||||
var allCampaignItemsUri = API.Marketing.GetAllCampaigns(_remoteServiceBaseUrl,
|
||||
pageSize, pageIndex);
|
||||
var uri = API.Marketing.GetAllCampaigns(_remoteServiceBaseUrl, pageSize, pageIndex);
|
||||
|
||||
var authorizationToken = await GetUserTokenAsync();
|
||||
var dataString = await _apiClient.GetStringAsync(allCampaignItemsUri, authorizationToken);
|
||||
var responseString = await _httpClient.GetStringAsync(uri);
|
||||
|
||||
var response = JsonConvert.DeserializeObject<Campaign>(dataString);
|
||||
var response = JsonConvert.DeserializeObject<Campaign>(responseString);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<CampaignItem> GetCampaignById(int id)
|
||||
{
|
||||
var campaignByIdItemUri = API.Marketing.GetAllCampaignById(_remoteServiceBaseUrl, id);
|
||||
var uri = API.Marketing.GetAllCampaignById(_remoteServiceBaseUrl, id);
|
||||
|
||||
var authorizationToken = await GetUserTokenAsync();
|
||||
var dataString = await _apiClient.GetStringAsync(campaignByIdItemUri, authorizationToken);
|
||||
var responseString = await _httpClient.GetStringAsync(uri);
|
||||
|
||||
var response = JsonConvert.DeserializeObject<CampaignItem>(dataString);
|
||||
var response = JsonConvert.DeserializeObject<CampaignItem>(responseString);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private string GetUserIdentity()
|
||||
{
|
||||
return _httpContextAccesor.HttpContext.User.FindFirst("sub").Value;
|
||||
}
|
||||
|
||||
private async Task<string> GetUserTokenAsync()
|
||||
{
|
||||
var context = _httpContextAccesor.HttpContext;
|
||||
return await context.GetTokenAsync("access_token");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
|
||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using WebMVC.Infrastructure;
|
||||
|
||||
@ -13,16 +13,16 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
{
|
||||
public class CatalogService : ICatalogService
|
||||
{
|
||||
private readonly IOptionsSnapshot<AppSettings> _settings;
|
||||
private readonly IHttpClient _apiClient;
|
||||
private readonly IOptions<AppSettings> _settings;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CatalogService> _logger;
|
||||
|
||||
private readonly string _remoteServiceBaseUrl;
|
||||
|
||||
public CatalogService(IOptionsSnapshot<AppSettings> settings, IHttpClient httpClient, ILogger<CatalogService> logger)
|
||||
public CatalogService(HttpClient httpClient, ILogger<CatalogService> logger, IOptions<AppSettings> settings)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_settings = settings;
|
||||
_apiClient = httpClient;
|
||||
_logger = logger;
|
||||
|
||||
_remoteServiceBaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1/c/catalog/";
|
||||
@ -30,25 +30,26 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
|
||||
public async Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type)
|
||||
{
|
||||
var allcatalogItemsUri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type);
|
||||
var uri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type);
|
||||
|
||||
var dataString = await _apiClient.GetStringAsync(allcatalogItemsUri);
|
||||
var responseString = await _httpClient.GetStringAsync(uri);
|
||||
|
||||
var response = JsonConvert.DeserializeObject<Catalog>(dataString);
|
||||
var catalog = JsonConvert.DeserializeObject<Catalog>(responseString);
|
||||
|
||||
return response;
|
||||
return catalog;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SelectListItem>> GetBrands()
|
||||
{
|
||||
var getBrandsUri = API.Catalog.GetAllBrands(_remoteServiceBaseUrl);
|
||||
var uri = API.Catalog.GetAllBrands(_remoteServiceBaseUrl);
|
||||
|
||||
var dataString = await _apiClient.GetStringAsync(getBrandsUri);
|
||||
var responseString = await _httpClient.GetStringAsync(uri);
|
||||
|
||||
var items = new List<SelectListItem>();
|
||||
|
||||
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true });
|
||||
|
||||
var brands = JArray.Parse(dataString);
|
||||
var brands = JArray.Parse(responseString);
|
||||
|
||||
foreach (var brand in brands.Children<JObject>())
|
||||
{
|
||||
@ -64,14 +65,14 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
|
||||
public async Task<IEnumerable<SelectListItem>> GetTypes()
|
||||
{
|
||||
var getTypesUri = API.Catalog.GetAllTypes(_remoteServiceBaseUrl);
|
||||
var uri = API.Catalog.GetAllTypes(_remoteServiceBaseUrl);
|
||||
|
||||
var dataString = await _apiClient.GetStringAsync(getTypesUri);
|
||||
var responseString = await _httpClient.GetStringAsync(uri);
|
||||
|
||||
var items = new List<SelectListItem>();
|
||||
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true });
|
||||
|
||||
var brands = JArray.Parse(dataString);
|
||||
var brands = JArray.Parse(responseString);
|
||||
foreach (var brand in brands.Children<JObject>())
|
||||
{
|
||||
items.Add(new SelectListItem()
|
||||
@ -80,6 +81,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
Text = brand.Value<string>("type")
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
|
||||
using Microsoft.eShopOnContainers.WebMVC;
|
||||
using Microsoft.eShopOnContainers.WebMVC;
|
||||
using Microsoft.eShopOnContainers.WebMVC.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using WebMVC.Infrastructure;
|
||||
using WebMVC.Models;
|
||||
@ -14,36 +12,27 @@ namespace WebMVC.Services
|
||||
{
|
||||
public class LocationService : ILocationService
|
||||
{
|
||||
private readonly IOptionsSnapshot<AppSettings> _settings;
|
||||
private readonly IHttpClient _apiClient;
|
||||
private readonly IOptions<AppSettings> _settings;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<CampaignService> _logger;
|
||||
private readonly string _remoteServiceBaseUrl;
|
||||
private readonly IHttpContextAccessor _httpContextAccesor;
|
||||
|
||||
public LocationService(IOptionsSnapshot<AppSettings> settings, IHttpClient httpClient,
|
||||
ILogger<CampaignService> logger, IHttpContextAccessor httpContextAccesor)
|
||||
public LocationService(HttpClient httpClient, IOptions<AppSettings> settings, ILogger<CampaignService> logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_settings = settings;
|
||||
_apiClient = httpClient;
|
||||
_logger = logger;
|
||||
|
||||
_remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/l/locations/";
|
||||
_httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor));
|
||||
}
|
||||
|
||||
public async Task CreateOrUpdateUserLocation(LocationDTO location)
|
||||
{
|
||||
var createOrUpdateUserLocationUri = API.Locations.CreateOrUpdateUserLocation(_remoteServiceBaseUrl);
|
||||
var uri = API.Locations.CreateOrUpdateUserLocation(_remoteServiceBaseUrl);
|
||||
var locationContent = new StringContent(JsonConvert.SerializeObject(location), System.Text.Encoding.UTF8, "application/json");
|
||||
|
||||
var authorizationToken = await GetUserTokenAsync();
|
||||
var response = await _apiClient.PostAsync(createOrUpdateUserLocationUri, location, authorizationToken);
|
||||
var response = await _httpClient.PostAsync(uri, locationContent);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
private async Task<string> GetUserTokenAsync()
|
||||
{
|
||||
var context = _httpContextAccesor.HttpContext;
|
||||
return await context.GetTokenAsync("access_token");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
|
||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
|
||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using WebMVC.Infrastructure;
|
||||
using WebMVC.Models;
|
||||
@ -14,69 +12,54 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
{
|
||||
public class OrderingService : IOrderingService
|
||||
{
|
||||
private IHttpClient _apiClient;
|
||||
private HttpClient _httpClient;
|
||||
private readonly string _remoteServiceBaseUrl;
|
||||
private readonly IOptionsSnapshot<AppSettings> _settings;
|
||||
private readonly IHttpContextAccessor _httpContextAccesor;
|
||||
private readonly IOptions<AppSettings> _settings;
|
||||
|
||||
public OrderingService(IOptionsSnapshot<AppSettings> settings, IHttpContextAccessor httpContextAccesor, IHttpClient httpClient)
|
||||
|
||||
public OrderingService(HttpClient httpClient, IOptions<AppSettings> settings)
|
||||
{
|
||||
_remoteServiceBaseUrl = $"{settings.Value.PurchaseUrl}/api/v1/o/orders";
|
||||
_httpClient = httpClient;
|
||||
_settings = settings;
|
||||
_httpContextAccesor = httpContextAccesor;
|
||||
_apiClient = httpClient;
|
||||
|
||||
_remoteServiceBaseUrl = $"{settings.Value.PurchaseUrl}/api/v1/o/orders";
|
||||
}
|
||||
|
||||
async public Task<Order> GetOrder(ApplicationUser user, string id)
|
||||
{
|
||||
var token = await GetUserTokenAsync();
|
||||
var getOrderUri = API.Order.GetOrder(_remoteServiceBaseUrl, id);
|
||||
var uri = API.Order.GetOrder(_remoteServiceBaseUrl, id);
|
||||
|
||||
var dataString = await _apiClient.GetStringAsync(getOrderUri, token);
|
||||
var responseString = await _httpClient.GetStringAsync(uri);
|
||||
|
||||
var response = JsonConvert.DeserializeObject<Order>(dataString);
|
||||
var response = JsonConvert.DeserializeObject<Order>(responseString);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async public Task<List<Order>> GetMyOrders(ApplicationUser user)
|
||||
{
|
||||
var token = await GetUserTokenAsync();
|
||||
var allMyOrdersUri = API.Order.GetAllMyOrders(_remoteServiceBaseUrl);
|
||||
var uri = API.Order.GetAllMyOrders(_remoteServiceBaseUrl);
|
||||
|
||||
var dataString = await _apiClient.GetStringAsync(allMyOrdersUri, token);
|
||||
var response = JsonConvert.DeserializeObject<List<Order>>(dataString);
|
||||
var responseString = await _httpClient.GetStringAsync(uri);
|
||||
|
||||
var response = JsonConvert.DeserializeObject<List<Order>>(responseString);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public Order MapUserInfoIntoOrder(ApplicationUser user, Order order)
|
||||
{
|
||||
order.City = user.City;
|
||||
order.Street = user.Street;
|
||||
order.State = user.State;
|
||||
order.Country = user.Country;
|
||||
order.ZipCode = user.ZipCode;
|
||||
|
||||
order.CardNumber = user.CardNumber;
|
||||
order.CardHolderName = user.CardHolderName;
|
||||
order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]), int.Parse(user.Expiration.Split('/')[0]), 1);
|
||||
order.CardSecurityNumber = user.SecurityNumber;
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
async public Task CancelOrder(string orderId)
|
||||
{
|
||||
var token = await GetUserTokenAsync();
|
||||
var order = new OrderDTO()
|
||||
{
|
||||
OrderNumber = orderId
|
||||
};
|
||||
|
||||
var cancelOrderUri = API.Order.CancelOrder(_remoteServiceBaseUrl);
|
||||
|
||||
var response = await _apiClient.PutAsync(cancelOrderUri, order, token, Guid.NewGuid().ToString());
|
||||
var uri = API.Order.CancelOrder(_remoteServiceBaseUrl);
|
||||
var orderContent = new StringContent(JsonConvert.SerializeObject(order), System.Text.Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _httpClient.PutAsync(uri, orderContent);
|
||||
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
|
||||
{
|
||||
@ -88,15 +71,15 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
|
||||
async public Task ShipOrder(string orderId)
|
||||
{
|
||||
var token = await GetUserTokenAsync();
|
||||
var order = new OrderDTO()
|
||||
{
|
||||
OrderNumber = orderId
|
||||
};
|
||||
|
||||
var shipOrderUri = API.Order.ShipOrder(_remoteServiceBaseUrl);
|
||||
var uri = API.Order.ShipOrder(_remoteServiceBaseUrl);
|
||||
var orderContent = new StringContent(JsonConvert.SerializeObject(order), System.Text.Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _apiClient.PutAsync(shipOrderUri, order, token, Guid.NewGuid().ToString());
|
||||
var response = await _httpClient.PutAsync(uri, orderContent);
|
||||
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
|
||||
{
|
||||
@ -120,6 +103,22 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
destination.CardSecurityNumber = original.CardSecurityNumber;
|
||||
}
|
||||
|
||||
public Order MapUserInfoIntoOrder(ApplicationUser user, Order order)
|
||||
{
|
||||
order.City = user.City;
|
||||
order.Street = user.Street;
|
||||
order.State = user.State;
|
||||
order.Country = user.Country;
|
||||
order.ZipCode = user.ZipCode;
|
||||
|
||||
order.CardNumber = user.CardNumber;
|
||||
order.CardHolderName = user.CardHolderName;
|
||||
order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]), int.Parse(user.Expiration.Split('/')[0]), 1);
|
||||
order.CardSecurityNumber = user.SecurityNumber;
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
public BasketDTO MapOrderToBasket(Order order)
|
||||
{
|
||||
order.CardExpirationApiFormat();
|
||||
@ -140,18 +139,5 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
||||
RequestId = order.RequestId
|
||||
};
|
||||
}
|
||||
|
||||
void SetFakeIdToProducts(Order order)
|
||||
{
|
||||
var id = 1;
|
||||
order.OrderItems.ForEach(x => { x.ProductId = id; id++; });
|
||||
}
|
||||
|
||||
async Task<string> GetUserTokenAsync()
|
||||
{
|
||||
var context = _httpContextAccesor.HttpContext;
|
||||
|
||||
return await context.GetTokenAsync("access_token");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
|
||||
using Microsoft.eShopOnContainers.WebMVC.Infrastructure;
|
||||
using Microsoft.eShopOnContainers.WebMVC.Services;
|
||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@ -15,15 +13,10 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.HealthChecks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Polly;
|
||||
using Polly.CircuitBreaker;
|
||||
using Polly.Extensions.Http;
|
||||
using Polly.Registry;
|
||||
using Polly.Retry;
|
||||
using Polly.Timeout;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net.Http;
|
||||
using WebMVC.Infrastructure;
|
||||
using WebMVC.Infrastructure.Middlewares;
|
||||
using WebMVC.Services;
|
||||
@ -151,6 +144,9 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
||||
|
||||
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddOptions();
|
||||
services.Configure<AppSettings>(configuration);
|
||||
|
||||
services.AddMvc();
|
||||
|
||||
services.AddSession();
|
||||
@ -163,124 +159,58 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
||||
})
|
||||
.PersistKeysToRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys");
|
||||
}
|
||||
|
||||
services.Configure<AppSettings>(configuration);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
// Adds all Http client services (like Service-Agents) using resilient Http requests based on HttpClient factory and Polly's policies
|
||||
public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// (CDLTLL) TEMPORAL COMMENT: Do we need this line of code if using HttpClient factory?
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
// (CDLTLL) I don't think we need this HttpClientDefaultPolicies if using fluent configuration ,as below...
|
||||
// Create a singleton object with the by default policies
|
||||
services.AddSingleton<HttpClientDefaultPolicies>();
|
||||
var defaultPolicies = services.BuildServiceProvider().GetService<HttpClientDefaultPolicies>();
|
||||
|
||||
// Create a Polly-Policy-Registry with the by default policies for resilient Http requests
|
||||
var pollyPolicyRegistry = services.AddPolicyRegistry();
|
||||
|
||||
|
||||
//Using fluent client configuration of Polly policies
|
||||
|
||||
var retriesWithExponentialBackoff = HttpPolicyExtensions
|
||||
.HandleTransientHttpError()
|
||||
.WaitAndRetryAsync(7,
|
||||
retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
|
||||
);
|
||||
//(CDLTLL) Instead of hardcoded values, use: configuration["HttpClientMaxNumberRetries"], configuration["SecondsBaseForExponentialBackoff"]
|
||||
|
||||
var retriesWithExponentialBackoff = HttpPolicyExtensions
|
||||
.HandleTransientHttpError()
|
||||
.WaitAndRetryAsync(7,retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
|
||||
|
||||
var circuitBreaker = HttpPolicyExtensions
|
||||
.HandleTransientHttpError()
|
||||
.CircuitBreakerAsync(6,
|
||||
TimeSpan.FromSeconds(30)
|
||||
);
|
||||
//(CDLTLL) Instead of hardcoded values, use: configuration["HttpClientExceptionsAllowedBeforeBreaking"], configuration["DurationOfBreakInMinutes"]
|
||||
.HandleTransientHttpError()
|
||||
.CircuitBreakerAsync(6, TimeSpan.FromSeconds(30));
|
||||
|
||||
|
||||
pollyPolicyRegistry.Add("DefaultRetriesWithExponentialBackoff", retriesWithExponentialBackoff);
|
||||
pollyPolicyRegistry.Add("DefaultCircuitBreaker", circuitBreaker);
|
||||
|
||||
|
||||
// (CDLTLL) Using "OLD" policies. I don't like it much...
|
||||
//pollyPolicyRegistry.Add("DefaultRetriesWithExponentialBackoff", defaultPolicies.GetWaitAndRetryPolicy()); // (CDLTLL) TEMPORAL COMMENT: Arguments here would be a good place to propagate the configuration values --> GetWaitAndRetryPolicy(configuration["HttpClientMaxNumberRetries"], configuration["SecondsBaseForExponentialBackoff"])
|
||||
//pollyPolicyRegistry.Add("DefaultCircuitBreaker", defaultPolicies.GetCircuitBreakerPolicy()); // (CDLTLL) TEMPORAL COMMENT: Arguments here would be a good place to propagate the configuration values --> GetCircuitBreakerPolicy(configuration["HttpClientExceptionsAllowedBeforeBreaking"], configuration["DurationOfBreakInMinutes"])
|
||||
|
||||
|
||||
|
||||
// (CDLTLL) Handlers need to be transient. //(CDLTLL) TEMPORAL COMMENT: This registration was missing, hence the error "InvalidOperationException: No service for type 'WebMVC.Infrastructure.HttpClientAuthorizationDelegatingHandler' has been registered"
|
||||
//register delegating handlers
|
||||
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
|
||||
services.AddTransient<HttpClientRequestIdDelegatingHandler>();
|
||||
|
||||
//Add all Typed-Clients (Service-Agents) through the HttpClient factory to implement Resilient Http requests
|
||||
//
|
||||
|
||||
//Add BasketService typed client (Service Agent)
|
||||
//add http client servicse
|
||||
services.AddHttpClient<IBasketService, BasketService>()
|
||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() //Additional Authentication-Delegating-Handler to add the OAuth-Bearer-token to the Http headers
|
||||
.AddPolicyHandlerFromRegistry("DefaultRetriesWithExponentialBackoff")
|
||||
.AddPolicyHandlerFromRegistry("DefaultCircuitBreaker");
|
||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
|
||||
.AddPolicyHandler(retriesWithExponentialBackoff)
|
||||
.AddPolicyHandler(circuitBreaker);
|
||||
|
||||
//Add CatalogService typed client (Service Agent)
|
||||
//services.AddHttpClient<ICatalogService, CatalogService>() ...
|
||||
services.AddHttpClient<ICatalogService, CatalogService>()
|
||||
.AddPolicyHandler(retriesWithExponentialBackoff)
|
||||
.AddPolicyHandler(circuitBreaker);
|
||||
|
||||
//Add OrderingService typed client (Service Agent)
|
||||
//services.AddHttpClient<IOrderingService, OrderingService>() ...
|
||||
services.AddHttpClient<IOrderingService, OrderingService>()
|
||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
|
||||
.AddHttpMessageHandler<HttpClientRequestIdDelegatingHandler>()
|
||||
.AddPolicyHandler(retriesWithExponentialBackoff)
|
||||
.AddPolicyHandler(circuitBreaker);
|
||||
|
||||
//Add CampaignService typed client (Service Agent)
|
||||
//services.AddHttpClient<ICampaignService, CampaignService>() ...
|
||||
services.AddHttpClient<ICampaignService, CampaignService>()
|
||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
|
||||
.AddPolicyHandler(retriesWithExponentialBackoff)
|
||||
.AddPolicyHandler(circuitBreaker);
|
||||
|
||||
//Add LocationService typed client (Service Agent)
|
||||
//services.AddHttpClient<ILocationService, LocationService>() ...
|
||||
services.AddHttpClient<ILocationService, LocationService>()
|
||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
|
||||
.AddPolicyHandler(retriesWithExponentialBackoff)
|
||||
.AddPolicyHandler(circuitBreaker);
|
||||
|
||||
//Add IdentityParser typed client (Service Agent)
|
||||
//services.AddHttpClient<IIdentityParser, IdentityParser>() ...
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// (CDLTLL) TEMPORAL COMMENT: The following Typed-Clients (Service-Agents) have to be coded as BasketService by using AddHttpClient<>(), right?
|
||||
// This code will be deleted
|
||||
services.AddTransient<ICatalogService, CatalogService>();
|
||||
services.AddTransient<IOrderingService, OrderingService>();
|
||||
|
||||
services.AddTransient<ICampaignService, CampaignService>();
|
||||
services.AddTransient<ILocationService, LocationService>();
|
||||
//add custom application services
|
||||
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TEMPORAL COMMENT: This code will be deleted when using HttpClientFactory, right?
|
||||
if (configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)
|
||||
{
|
||||
services.AddSingleton<IResilientHttpClientFactory, ResilientHttpClientFactory>(sp =>
|
||||
{
|
||||
var logger = sp.GetRequiredService<ILogger<ResilientHttpClient>>();
|
||||
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
|
||||
|
||||
var retryCount = 6;
|
||||
if (!string.IsNullOrEmpty(configuration["HttpClientRetryCount"]))
|
||||
{
|
||||
retryCount = int.Parse(configuration["HttpClientRetryCount"]);
|
||||
}
|
||||
|
||||
var exceptionsAllowedBeforeBreaking = 5;
|
||||
if (!string.IsNullOrEmpty(configuration["HttpClientExceptionsAllowedBeforeBreaking"]))
|
||||
{
|
||||
exceptionsAllowedBeforeBreaking = int.Parse(configuration["HttpClientExceptionsAllowedBeforeBreaking"]);
|
||||
}
|
||||
|
||||
return new ResilientHttpClientFactory(logger, httpContextAccessor, exceptionsAllowedBeforeBreaking, retryCount);
|
||||
});
|
||||
services.AddSingleton<IHttpClient, ResilientHttpClient>(sp => sp.GetService<IResilientHttpClientFactory>().CreateResilientHttpClient());
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<IHttpClient, StandardHttpClient>();
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
return services;
|
||||
}
|
||||
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||
|
Loading…
x
Reference in New Issue
Block a user