Added sample for use new IHttpClientFactory feature on WebMVC
This commit is contained in:
parent
36fb20acea
commit
cf4b7ff47f
@ -71,7 +71,6 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
|
|||||||
{
|
{
|
||||||
var user = _appUserParser.Parse(HttpContext.User);
|
var user = _appUserParser.Parse(HttpContext.User);
|
||||||
await _basketSvc.AddItemToBasket(user, productDetails.Id);
|
await _basketSvc.AddItemToBasket(user, productDetails.Id);
|
||||||
//await _basketSvc.AddItemToBasket(user, product);
|
|
||||||
}
|
}
|
||||||
return RedirectToAction("Index", "Catalog");
|
return RedirectToAction("Index", "Catalog");
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace WebMVC.Infrastructure
|
||||||
|
{
|
||||||
|
public class HttpClientAuthorizationDelegatingHandler
|
||||||
|
: DelegatingHandler
|
||||||
|
{
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccesor;
|
||||||
|
|
||||||
|
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccesor)
|
||||||
|
{
|
||||||
|
_httpContextAccesor = httpContextAccesor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var authorizationHeader = _httpContextAccesor.HttpContext
|
||||||
|
.Request.Headers["Authorization"];
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(authorizationHeader))
|
||||||
|
{
|
||||||
|
request.Headers.Add("Authorization", new List<string>() { authorizationHeader });
|
||||||
|
}
|
||||||
|
|
||||||
|
var token = await GetToken();
|
||||||
|
|
||||||
|
if (token != null)
|
||||||
|
{
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await base.SendAsync(request, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<string> GetToken()
|
||||||
|
{
|
||||||
|
const string ACCESS_TOKEN = "access_token";
|
||||||
|
|
||||||
|
return await _httpContextAccesor.HttpContext
|
||||||
|
.GetTokenAsync(ACCESS_TOKEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
src/Web/WebMVC/Infrastructure/HttpClientDefaultPolicies.cs
Normal file
60
src/Web/WebMVC/Infrastructure/HttpClientDefaultPolicies.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Polly;
|
||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
|
namespace WebMVC.Infrastructure
|
||||||
|
{
|
||||||
|
public class HttpClientDefaultPolicies
|
||||||
|
{
|
||||||
|
const int RETRY_COUNT = 6;
|
||||||
|
const int EXCEPTIONS_ALLOWED_BEFORE_CIRCUIT_BREAKER = 5;
|
||||||
|
|
||||||
|
private readonly ILogger<HttpClientDefaultPolicies> _logger;
|
||||||
|
|
||||||
|
public HttpClientDefaultPolicies(ILogger<HttpClientDefaultPolicies> logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Policy GetWaitAndRetryPolicy()
|
||||||
|
{
|
||||||
|
return Policy.Handle<HttpRequestException>()
|
||||||
|
.WaitAndRetryAsync(
|
||||||
|
// number of retries
|
||||||
|
RETRY_COUNT,
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Policy GetCircuitBreakerPolicy()
|
||||||
|
{
|
||||||
|
return Policy.Handle<HttpRequestException>()
|
||||||
|
.CircuitBreakerAsync(
|
||||||
|
// number of exceptions before breaking circuit
|
||||||
|
EXCEPTIONS_ALLOWED_BEFORE_CIRCUIT_BREAKER,
|
||||||
|
// 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");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,9 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
|
|
||||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using WebMVC.Infrastructure;
|
using WebMVC.Infrastructure;
|
||||||
using WebMVC.Models;
|
using WebMVC.Models;
|
||||||
@ -15,29 +13,26 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
|||||||
public class BasketService : IBasketService
|
public class BasketService : IBasketService
|
||||||
{
|
{
|
||||||
private readonly IOptionsSnapshot<AppSettings> _settings;
|
private readonly IOptionsSnapshot<AppSettings> _settings;
|
||||||
private readonly IHttpClient _apiClient;
|
private readonly HttpClient _apiClient;
|
||||||
private readonly string _basketByPassUrl;
|
private readonly string _basketByPassUrl;
|
||||||
private readonly string _purchaseUrl;
|
private readonly string _purchaseUrl;
|
||||||
private readonly IHttpContextAccessor _httpContextAccesor;
|
|
||||||
|
|
||||||
private readonly string _bffUrl;
|
private readonly string _bffUrl;
|
||||||
|
|
||||||
public BasketService(IOptionsSnapshot<AppSettings> settings,
|
public BasketService(HttpClient httpClient,IOptionsSnapshot<AppSettings> settings)
|
||||||
IHttpContextAccessor httpContextAccesor, IHttpClient httpClient)
|
|
||||||
{
|
{
|
||||||
|
_apiClient = httpClient;
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
|
|
||||||
_basketByPassUrl = $"{_settings.Value.PurchaseUrl}/api/v1/b/basket";
|
_basketByPassUrl = $"{_settings.Value.PurchaseUrl}/api/v1/b/basket";
|
||||||
_purchaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1";
|
_purchaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1";
|
||||||
_httpContextAccesor = httpContextAccesor;
|
|
||||||
_apiClient = httpClient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Basket> GetBasket(ApplicationUser user)
|
public async Task<Basket> GetBasket(ApplicationUser user)
|
||||||
{
|
{
|
||||||
var token = await GetUserTokenAsync();
|
|
||||||
var getBasketUri = API.Basket.GetBasket(_basketByPassUrl, user.Id);
|
var getBasketUri = API.Basket.GetBasket(_basketByPassUrl, user.Id);
|
||||||
|
|
||||||
var dataString = await _apiClient.GetStringAsync(getBasketUri, token);
|
var dataString = await _apiClient.GetStringAsync(getBasketUri);
|
||||||
|
|
||||||
return string.IsNullOrEmpty(dataString) ?
|
return string.IsNullOrEmpty(dataString) ?
|
||||||
new Basket() { BuyerId = user.Id} :
|
new Basket() { BuyerId = user.Id} :
|
||||||
@ -46,10 +41,10 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
|||||||
|
|
||||||
public async Task<Basket> UpdateBasket(Basket basket)
|
public async Task<Basket> UpdateBasket(Basket basket)
|
||||||
{
|
{
|
||||||
var token = await GetUserTokenAsync();
|
|
||||||
var updateBasketUri = API.Basket.UpdateBasket(_basketByPassUrl);
|
var updateBasketUri = API.Basket.UpdateBasket(_basketByPassUrl);
|
||||||
|
var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
var response = await _apiClient.PostAsync(updateBasketUri, basket, token);
|
var response = await _apiClient.PostAsync(updateBasketUri, content);
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
@ -58,10 +53,10 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
|||||||
|
|
||||||
public async Task Checkout(BasketDTO basket)
|
public async Task Checkout(BasketDTO basket)
|
||||||
{
|
{
|
||||||
var token = await GetUserTokenAsync();
|
|
||||||
var updateBasketUri = API.Basket.CheckoutBasket(_basketByPassUrl);
|
var updateBasketUri = API.Basket.CheckoutBasket(_basketByPassUrl);
|
||||||
|
var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
var response = await _apiClient.PostAsync(updateBasketUri, basket, token);
|
var response = await _apiClient.PostAsync(updateBasketUri, content);
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
}
|
}
|
||||||
@ -69,54 +64,52 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
|
|||||||
public async Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities)
|
public async Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities)
|
||||||
{
|
{
|
||||||
|
|
||||||
var token = await GetUserTokenAsync();
|
|
||||||
var updateBasketUri = API.Purchase.UpdateBasketItem(_purchaseUrl);
|
var updateBasketUri = API.Purchase.UpdateBasketItem(_purchaseUrl);
|
||||||
var userId = user.Id;
|
var basketUpdate = new
|
||||||
|
|
||||||
var response = await _apiClient.PutAsync(updateBasketUri, new
|
|
||||||
{
|
{
|
||||||
BasketId = userId,
|
BasketId = user.Id,
|
||||||
Updates = quantities.Select(kvp => new
|
Updates = quantities.Select(kvp => new
|
||||||
{
|
{
|
||||||
BasketItemId = kvp.Key,
|
BasketItemId = kvp.Key,
|
||||||
NewQty = kvp.Value
|
NewQty = kvp.Value
|
||||||
}).ToArray()
|
}).ToArray()
|
||||||
}, token);
|
};
|
||||||
|
|
||||||
|
var content = new StringContent(JsonConvert.SerializeObject(basketUpdate), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
var response = await _apiClient.PutAsync(updateBasketUri,content);
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var jsonResponse = await response.Content.ReadAsStringAsync();
|
var jsonResponse = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
return JsonConvert.DeserializeObject<Basket>(jsonResponse);
|
return JsonConvert.DeserializeObject<Basket>(jsonResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Order> GetOrderDraft(string basketId)
|
public async Task<Order> GetOrderDraft(string basketId)
|
||||||
{
|
{
|
||||||
var token = await GetUserTokenAsync();
|
|
||||||
var draftOrderUri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId);
|
var draftOrderUri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId);
|
||||||
var json = await _apiClient.GetStringAsync(draftOrderUri, token);
|
var response = await _apiClient.GetStringAsync(draftOrderUri);
|
||||||
return JsonConvert.DeserializeObject<Order>(json);
|
|
||||||
|
return JsonConvert.DeserializeObject<Order>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public async Task AddItemToBasket(ApplicationUser user, int productId)
|
public async Task AddItemToBasket(ApplicationUser user, int productId)
|
||||||
{
|
{
|
||||||
var token = await GetUserTokenAsync();
|
|
||||||
var updateBasketUri = API.Purchase.AddItemToBasket(_purchaseUrl);
|
var updateBasketUri = API.Purchase.AddItemToBasket(_purchaseUrl);
|
||||||
var userId = user.Id;
|
|
||||||
|
|
||||||
var response = await _apiClient.PostAsync(updateBasketUri, new
|
var newItem = new
|
||||||
{
|
{
|
||||||
CatalogItemId = productId,
|
CatalogItemId = productId,
|
||||||
BasketId = userId,
|
BasketId = user.Id,
|
||||||
Quantity = 1
|
Quantity = 1
|
||||||
}, token);
|
};
|
||||||
|
|
||||||
}
|
var content = new StringContent(JsonConvert.SerializeObject(newItem), System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
async Task<string> GetUserTokenAsync()
|
var response = await _apiClient.PostAsync(updateBasketUri, content);
|
||||||
{
|
|
||||||
var context = _httpContextAccesor.HttpContext;
|
|
||||||
return await context.GetTokenAsync("access_token");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,12 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.HealthChecks;
|
using Microsoft.Extensions.HealthChecks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Polly;
|
||||||
|
using Polly.Registry;
|
||||||
using StackExchange.Redis;
|
using StackExchange.Redis;
|
||||||
using System;
|
using System;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Net.Http;
|
||||||
using WebMVC.Infrastructure;
|
using WebMVC.Infrastructure;
|
||||||
using WebMVC.Infrastructure.Middlewares;
|
using WebMVC.Infrastructure.Middlewares;
|
||||||
using WebMVC.Services;
|
using WebMVC.Services;
|
||||||
@ -35,104 +38,11 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
|||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
RegisterAppInsights(services);
|
services.AddAppInsight(Configuration)
|
||||||
|
.AddHealthChecks(Configuration)
|
||||||
services.AddMvc();
|
.AddCustomMvc(Configuration)
|
||||||
|
.AddCustomApplicationServices(Configuration)
|
||||||
services.AddSession();
|
.AddCustomAuthentication(Configuration);
|
||||||
|
|
||||||
if (Configuration.GetValue<string>("IsClusterEnv") == bool.TrueString)
|
|
||||||
{
|
|
||||||
services.AddDataProtection(opts =>
|
|
||||||
{
|
|
||||||
opts.ApplicationDiscriminator = "eshop.webmvc";
|
|
||||||
})
|
|
||||||
.PersistKeysToRedis(ConnectionMultiplexer.Connect(Configuration["DPConnectionString"]), "DataProtection-Keys");
|
|
||||||
}
|
|
||||||
|
|
||||||
services.Configure<AppSettings>(Configuration);
|
|
||||||
|
|
||||||
services.AddHealthChecks(checks =>
|
|
||||||
{
|
|
||||||
var minutes = 1;
|
|
||||||
if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed))
|
|
||||||
{
|
|
||||||
minutes = minutesParsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
checks.AddUrlCheck(Configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes));
|
|
||||||
checks.AddUrlCheck(Configuration["OrderingUrlHC"], TimeSpan.FromMinutes(minutes));
|
|
||||||
checks.AddUrlCheck(Configuration["BasketUrlHC"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos
|
|
||||||
checks.AddUrlCheck(Configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes));
|
|
||||||
checks.AddUrlCheck(Configuration["MarketingUrlHC"], TimeSpan.FromMinutes(minutes));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add application services.
|
|
||||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
|
||||||
services.AddTransient<ICatalogService, CatalogService>();
|
|
||||||
services.AddTransient<IOrderingService, OrderingService>();
|
|
||||||
services.AddTransient<IBasketService, BasketService>();
|
|
||||||
services.AddTransient<ICampaignService, CampaignService>();
|
|
||||||
services.AddTransient<ILocationService, LocationService>();
|
|
||||||
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
|
|
||||||
|
|
||||||
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>();
|
|
||||||
}
|
|
||||||
var useLoadTest = Configuration.GetValue<bool>("UseLoadTest");
|
|
||||||
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
|
|
||||||
var callBackUrl = Configuration.GetValue<string>("CallBackUrl");
|
|
||||||
|
|
||||||
// Add Authentication services
|
|
||||||
|
|
||||||
services.AddAuthentication(options => {
|
|
||||||
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
|
||||||
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
|
|
||||||
})
|
|
||||||
.AddCookie()
|
|
||||||
.AddOpenIdConnect(options => {
|
|
||||||
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
|
||||||
options.Authority = identityUrl.ToString();
|
|
||||||
options.SignedOutRedirectUri = callBackUrl.ToString();
|
|
||||||
options.ClientId = useLoadTest ? "mvctest" : "mvc";
|
|
||||||
options.ClientSecret = "secret";
|
|
||||||
options.ResponseType = useLoadTest ? "code id_token token" : "code id_token";
|
|
||||||
options.SaveTokens = true;
|
|
||||||
options.GetClaimsFromUserInfoEndpoint = true;
|
|
||||||
options.RequireHttpsMetadata = false;
|
|
||||||
options.Scope.Add("openid");
|
|
||||||
options.Scope.Add("profile");
|
|
||||||
options.Scope.Add("orders");
|
|
||||||
options.Scope.Add("basket");
|
|
||||||
options.Scope.Add("marketing");
|
|
||||||
options.Scope.Add("locations");
|
|
||||||
options.Scope.Add("webshoppingagg");
|
|
||||||
options.Scope.Add("orders.signalrhub");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
@ -140,8 +50,6 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
|||||||
{
|
{
|
||||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
||||||
|
|
||||||
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
|
||||||
loggerFactory.AddDebug();
|
|
||||||
loggerFactory.AddAzureWebAppDiagnostics();
|
loggerFactory.AddAzureWebAppDiagnostics();
|
||||||
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
|
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
|
||||||
|
|
||||||
@ -173,7 +81,7 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
|||||||
{
|
{
|
||||||
app.UseMiddleware<ByPassAuthMiddleware>();
|
app.UseMiddleware<ByPassAuthMiddleware>();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
|
|
||||||
var log = loggerFactory.CreateLogger("identity");
|
var log = loggerFactory.CreateLogger("identity");
|
||||||
@ -191,23 +99,162 @@ namespace Microsoft.eShopOnContainers.WebMVC
|
|||||||
template: "{controller=Error}/{action=Error}");
|
template: "{controller=Error}/{action=Error}");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void RegisterAppInsights(IServiceCollection services)
|
static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.AddApplicationInsightsTelemetry(Configuration);
|
services.AddApplicationInsightsTelemetry(configuration);
|
||||||
var orchestratorType = Configuration.GetValue<string>("OrchestratorType");
|
var orchestratorType = configuration.GetValue<string>("OrchestratorType");
|
||||||
|
|
||||||
if (orchestratorType?.ToUpper() == "K8S")
|
if (orchestratorType?.ToUpper() == "K8S")
|
||||||
{
|
{
|
||||||
// Enable K8s telemetry initializer
|
// Enable K8s telemetry initializer
|
||||||
services.EnableKubernetes();
|
services.EnableKubernetes();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (orchestratorType?.ToUpper() == "SF")
|
if (orchestratorType?.ToUpper() == "SF")
|
||||||
{
|
{
|
||||||
// Enable SF telemetry initializer
|
// Enable SF telemetry initializer
|
||||||
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
|
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
|
||||||
new FabricTelemetryInitializer());
|
new FabricTelemetryInitializer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddHealthChecks(checks =>
|
||||||
|
{
|
||||||
|
var minutes = 1;
|
||||||
|
if (int.TryParse(configuration["HealthCheck:Timeout"], out var minutesParsed))
|
||||||
|
{
|
||||||
|
minutes = minutesParsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
checks.AddUrlCheck(configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes));
|
||||||
|
checks.AddUrlCheck(configuration["OrderingUrlHC"], TimeSpan.FromMinutes(minutes));
|
||||||
|
checks.AddUrlCheck(configuration["BasketUrlHC"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos
|
||||||
|
checks.AddUrlCheck(configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes));
|
||||||
|
checks.AddUrlCheck(configuration["MarketingUrlHC"], TimeSpan.FromMinutes(minutes));
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddMvc();
|
||||||
|
|
||||||
|
services.AddSession();
|
||||||
|
|
||||||
|
if (configuration.GetValue<string>("IsClusterEnv") == bool.TrueString)
|
||||||
|
{
|
||||||
|
services.AddDataProtection(opts =>
|
||||||
|
{
|
||||||
|
opts.ApplicationDiscriminator = "eshop.webmvc";
|
||||||
|
})
|
||||||
|
.PersistKeysToRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys");
|
||||||
|
}
|
||||||
|
|
||||||
|
services.Configure<AppSettings>(configuration);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddCustomApplicationServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
|
services.AddSingleton<HttpClientDefaultPolicies>();
|
||||||
|
var defaultPolicies = services.BuildServiceProvider().GetService<HttpClientDefaultPolicies>();
|
||||||
|
|
||||||
|
var registry = services.AddPolicyRegistry();
|
||||||
|
registry.Add("WaitAndRetry", defaultPolicies.GetWaitAndRetryPolicy());
|
||||||
|
registry.Add("CircuitBreaker", defaultPolicies.GetCircuitBreakerPolicy());
|
||||||
|
|
||||||
|
services.AddHttpClient<IBasketService, BasketService>()
|
||||||
|
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
|
||||||
|
.AddPolicyHandlerFromRegistry("WaitAndRetry")
|
||||||
|
.AddPolicyHandlerFromRegistry("CircuitBreaker");
|
||||||
|
|
||||||
|
services.AddTransient<ICatalogService, CatalogService>();
|
||||||
|
services.AddTransient<IOrderingService, OrderingService>();
|
||||||
|
|
||||||
|
services.AddTransient<ICampaignService, CampaignService>();
|
||||||
|
services.AddTransient<ILocationService, LocationService>();
|
||||||
|
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var useLoadTest = configuration.GetValue<bool>("UseLoadTest");
|
||||||
|
var identityUrl = configuration.GetValue<string>("IdentityUrl");
|
||||||
|
var callBackUrl = configuration.GetValue<string>("CallBackUrl");
|
||||||
|
|
||||||
|
// Add Authentication services
|
||||||
|
|
||||||
|
services.AddAuthentication(options =>
|
||||||
|
{
|
||||||
|
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||||
|
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
|
||||||
|
})
|
||||||
|
.AddCookie()
|
||||||
|
.AddOpenIdConnect(options =>
|
||||||
|
{
|
||||||
|
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||||
|
options.Authority = identityUrl.ToString();
|
||||||
|
options.SignedOutRedirectUri = callBackUrl.ToString();
|
||||||
|
options.ClientId = useLoadTest ? "mvctest" : "mvc";
|
||||||
|
options.ClientSecret = "secret";
|
||||||
|
options.ResponseType = useLoadTest ? "code id_token token" : "code id_token";
|
||||||
|
options.SaveTokens = true;
|
||||||
|
options.GetClaimsFromUserInfoEndpoint = true;
|
||||||
|
options.RequireHttpsMetadata = false;
|
||||||
|
options.Scope.Add("openid");
|
||||||
|
options.Scope.Add("profile");
|
||||||
|
options.Scope.Add("orders");
|
||||||
|
options.Scope.Add("basket");
|
||||||
|
options.Scope.Add("marketing");
|
||||||
|
options.Scope.Add("locations");
|
||||||
|
options.Scope.Add("webshoppingagg");
|
||||||
|
options.Scope.Add("orders.signalrhub");
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
|
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
|
||||||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
|
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" />
|
||||||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
|
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1-beta1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.1.0-rc1-final" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0-rc1-final" />
|
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.0-rc1-final" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-rc1-final" />
|
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-rc1-final" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.3" />
|
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.3" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user