@ -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 Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.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); | |||
} | |||
} | |||
} |
@ -1,53 +1,41 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Microsoft.AspNetCore.Authentication; | |||
using Microsoft.AspNetCore.Http; | |||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||
using Microsoft.Extensions.Logging; | |||
using Microsoft.Extensions.Options; | |||
using Newtonsoft.Json; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||
using System.Net.Http; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services | |||
{ | |||
public class BasketService : IBasketService | |||
{ | |||
private readonly IHttpClient _apiClient; | |||
private readonly HttpClient _httpClient; | |||
private readonly ILogger<BasketService> _logger; | |||
private readonly UrlsConfig _urls; | |||
private readonly IHttpContextAccessor _httpContextAccessor; | |||
public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger<BasketService> logger, IOptionsSnapshot<UrlsConfig> config) | |||
public BasketService(HttpClient httpClient, ILogger<BasketService> logger, IOptions<UrlsConfig> config) | |||
{ | |||
_apiClient = httpClient; | |||
_httpClient = httpClient; | |||
_logger = logger; | |||
_urls = config.Value; | |||
_httpContextAccessor = httpContextAccessor; | |||
} | |||
public async Task<BasketData> GetById(string id) | |||
{ | |||
var token = await GetUserTokenAsync(); | |||
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id), token); | |||
var data = await _httpClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id)); | |||
var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null; | |||
return basket; | |||
} | |||
public async Task Update(BasketData currentBasket) | |||
{ | |||
var token = await GetUserTokenAsync(); | |||
var data = await _apiClient.PostAsync<BasketData>(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), currentBasket, token); | |||
int i = 0; | |||
} | |||
var basketContent = new StringContent(JsonConvert.SerializeObject(currentBasket), System.Text.Encoding.UTF8, "application/json"); | |||
async Task<string> GetUserTokenAsync() | |||
{ | |||
var context = _httpContextAccessor.HttpContext; | |||
return await context.GetTokenAsync("access_token"); | |||
var data = await _httpClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), basketContent); | |||
} | |||
} | |||
} |
@ -1,43 +1,41 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||
using Microsoft.Extensions.Logging; | |||
using Microsoft.Extensions.Options; | |||
using Newtonsoft.Json; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; | |||
using System.Collections.Generic; | |||
using System.Net.Http; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services | |||
{ | |||
public class CatalogService : ICatalogService | |||
{ | |||
private readonly IHttpClient _apiClient; | |||
private readonly HttpClient _httpClient; | |||
private readonly ILogger<CatalogService> _logger; | |||
private readonly UrlsConfig _urls; | |||
public CatalogService(IHttpClient httpClient, ILogger<CatalogService> logger, IOptionsSnapshot<UrlsConfig> config) | |||
public CatalogService(HttpClient httpClient, ILogger<CatalogService> logger, IOptions<UrlsConfig> config) | |||
{ | |||
_apiClient = httpClient; | |||
_httpClient = httpClient; | |||
_logger = logger; | |||
_urls = config.Value; | |||
} | |||
public async Task<CatalogItem> GetCatalogItem(int id) | |||
{ | |||
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); | |||
var item = JsonConvert.DeserializeObject<CatalogItem>(data); | |||
return item; | |||
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); | |||
var catalogItem = JsonConvert.DeserializeObject<CatalogItem>(stringContent); | |||
return catalogItem; | |||
} | |||
public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids) | |||
{ | |||
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); | |||
var item = JsonConvert.DeserializeObject<CatalogItem[]>(data); | |||
return item; | |||
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); | |||
var catalogItems = JsonConvert.DeserializeObject<CatalogItem[]>(stringContent); | |||
return catalogItems; | |||
} | |||
} | |||
} |
@ -1,138 +1,183 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IdentityModel.Tokens.Jwt; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Microsoft.AspNetCore.Authentication.JwtBearer; | |||
using Microsoft.AspNetCore.Authentication.JwtBearer; | |||
using Microsoft.AspNetCore.Builder; | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.AspNetCore.Http; | |||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||
using Microsoft.Extensions.Configuration; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Logging; | |||
using Microsoft.Extensions.Options; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure; | |||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; | |||
using Microsoft.Extensions.Configuration; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Logging; | |||
using Polly; | |||
using Polly.Extensions.Http; | |||
using Swashbuckle.AspNetCore.Swagger; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IdentityModel.Tokens.Jwt; | |||
using System.Net.Http; | |||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator | |||
{ | |||
public class Startup | |||
{ | |||
public Startup(IConfiguration configuration) | |||
{ | |||
Configuration = configuration; | |||
} | |||
public IConfiguration Configuration { get; } | |||
// This method gets called by the runtime. Use this method to add services to the container. | |||
public void ConfigureServices(IServiceCollection services) | |||
{ | |||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||
services.AddSingleton<IHttpClient, StandardHttpClient>(); | |||
services.AddTransient<ICatalogService, CatalogService>(); | |||
services.AddTransient<IBasketService, BasketService>(); | |||
services.AddTransient<IOrderApiClient, OrderApiClient>(); | |||
services.AddOptions(); | |||
services.Configure<UrlsConfig>(Configuration.GetSection("urls")); | |||
services.AddMvc(); | |||
services.AddSwaggerGen(options => | |||
{ | |||
options.DescribeAllEnumsAsStrings(); | |||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info | |||
{ | |||
Title = "Shopping Aggregator for Mobile Clients", | |||
Version = "v1", | |||
Description = "Shopping Aggregator for Mobile Clients", | |||
TermsOfService = "Terms Of Service" | |||
}); | |||
options.AddSecurityDefinition("oauth2", new OAuth2Scheme | |||
{ | |||
Type = "oauth2", | |||
Flow = "implicit", | |||
AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize", | |||
TokenUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token", | |||
Scopes = new Dictionary<string, string>() | |||
{ | |||
{ "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" } | |||
} | |||
}); | |||
options.OperationFilter<AuthorizeCheckOperationFilter>(); | |||
}); | |||
services.AddCors(options => | |||
{ | |||
options.AddPolicy("CorsPolicy", | |||
builder => builder.AllowAnyOrigin() | |||
.AllowAnyMethod() | |||
.AllowAnyHeader() | |||
.AllowCredentials()); | |||
}); | |||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); | |||
var identityUrl = Configuration.GetValue<string>("urls:identity"); | |||
services.AddAuthentication(options => | |||
{ | |||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; | |||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; | |||
}).AddJwtBearer(options => | |||
{ | |||
options.Authority = identityUrl; | |||
options.RequireHttpsMetadata = false; | |||
options.Audience = "mobileshoppingagg"; | |||
options.Events = new JwtBearerEvents() | |||
{ | |||
OnAuthenticationFailed = async ctx => | |||
{ | |||
int i = 0; | |||
}, | |||
OnTokenValidated = async ctx => | |||
{ | |||
int i = 0; | |||
} | |||
}; | |||
}); | |||
} | |||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |||
{ | |||
var pathBase = Configuration["PATH_BASE"]; | |||
if (!string.IsNullOrEmpty(pathBase)) | |||
{ | |||
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); | |||
app.UsePathBase(pathBase); | |||
} | |||
app.UseCors("CorsPolicy"); | |||
if (env.IsDevelopment()) | |||
{ | |||
app.UseDeveloperExceptionPage(); | |||
} | |||
app.UseAuthentication(); | |||
app.UseMvc(); | |||
app.UseSwagger().UseSwaggerUI(c => | |||
{ | |||
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); | |||
c.ConfigureOAuth2("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI"); | |||
}); | |||
} | |||
} | |||
public class Startup | |||
{ | |||
public Startup(IConfiguration configuration) | |||
{ | |||
Configuration = configuration; | |||
} | |||
public IConfiguration Configuration { get; } | |||
// This method gets called by the runtime. Use this method to add services to the container. | |||
public void ConfigureServices(IServiceCollection services) | |||
{ | |||
services.AddCustomMvc(Configuration) | |||
.AddCustomAuthentication(Configuration) | |||
.AddHttpServices(); | |||
} | |||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |||
{ | |||
var pathBase = Configuration["PATH_BASE"]; | |||
if (!string.IsNullOrEmpty(pathBase)) | |||
{ | |||
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); | |||
app.UsePathBase(pathBase); | |||
} | |||
app.UseCors("CorsPolicy"); | |||
if (env.IsDevelopment()) | |||
{ | |||
app.UseDeveloperExceptionPage(); | |||
} | |||
app.UseAuthentication(); | |||
app.UseMvc(); | |||
app.UseSwagger().UseSwaggerUI(c => | |||
{ | |||
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); | |||
c.ConfigureOAuth2("Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI"); | |||
}); | |||
} | |||
} | |||
public static class ServiceCollectionExtensions | |||
{ | |||
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddOptions(); | |||
services.Configure<UrlsConfig>(configuration.GetSection("urls")); | |||
services.AddMvc().SetCompatibilityVersion(AspNetCore.Mvc.CompatibilityVersion.Version_2_1); | |||
services.AddSwaggerGen(options => | |||
{ | |||
options.DescribeAllEnumsAsStrings(); | |||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info | |||
{ | |||
Title = "Shopping Aggregator for Mobile Clients", | |||
Version = "v1", | |||
Description = "Shopping Aggregator for Mobile Clients", | |||
TermsOfService = "Terms Of Service" | |||
}); | |||
options.AddSecurityDefinition("oauth2", new OAuth2Scheme | |||
{ | |||
Type = "oauth2", | |||
Flow = "implicit", | |||
AuthorizationUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize", | |||
TokenUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token", | |||
Scopes = new Dictionary<string, string>() | |||
{ | |||
{ "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" } | |||
} | |||
}); | |||
options.OperationFilter<AuthorizeCheckOperationFilter>(); | |||
}); | |||
services.AddCors(options => | |||
{ | |||
options.AddPolicy("CorsPolicy", | |||
builder => builder.AllowAnyOrigin() | |||
.AllowAnyMethod() | |||
.AllowAnyHeader() | |||
.AllowCredentials()); | |||
}); | |||
return services; | |||
} | |||
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); | |||
var identityUrl = configuration.GetValue<string>("urls:identity"); | |||
services.AddAuthentication(options => | |||
{ | |||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; | |||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; | |||
}).AddJwtBearer(options => | |||
{ | |||
options.Authority = identityUrl; | |||
options.RequireHttpsMetadata = false; | |||
options.Audience = "mobileshoppingagg"; | |||
options.Events = new JwtBearerEvents() | |||
{ | |||
OnAuthenticationFailed = async ctx => | |||
{ | |||
int i = 0; | |||
}, | |||
OnTokenValidated = async ctx => | |||
{ | |||
int i = 0; | |||
} | |||
}; | |||
}); | |||
return services; | |||
} | |||
public static IServiceCollection AddHttpServices(this IServiceCollection services) | |||
{ | |||
//register delegating handlers | |||
services.AddTransient<HttpClientAuthorizationDelegatingHandler>(); | |||
services.AddHttpContextAccessor(); | |||
//register http services | |||
services.AddHttpClient<IBasketService, BasketService>() | |||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() | |||
.AddPolicyHandler(GetRetryPolicy()) | |||
.AddPolicyHandler(GetCircuitBreakerPolicy()); | |||
services.AddHttpClient<ICatalogService, CatalogService>() | |||
.AddPolicyHandler(GetRetryPolicy()) | |||
.AddPolicyHandler(GetCircuitBreakerPolicy()); | |||
services.AddHttpClient<IOrderApiClient, OrderApiClient>() | |||
.AddPolicyHandler(GetRetryPolicy()) | |||
.AddPolicyHandler(GetCircuitBreakerPolicy()); | |||
return services; | |||
} | |||
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() | |||
{ | |||
return HttpPolicyExtensions | |||
.HandleTransientHttpError() | |||
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) | |||
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); | |||
} | |||
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy() | |||
{ | |||
return HttpPolicyExtensions | |||
.HandleTransientHttpError() | |||
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); | |||
} | |||
} | |||
} |
@ -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 Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.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); | |||
} | |||
} | |||
} |
@ -1,53 +1,39 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Microsoft.AspNetCore.Authentication; | |||
using Microsoft.AspNetCore.Http; | |||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; | |||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||
using Microsoft.Extensions.Logging; | |||
using Microsoft.Extensions.Options; | |||
using Newtonsoft.Json; | |||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; | |||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||
using System.Net.Http; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services | |||
{ | |||
public class BasketService : IBasketService | |||
{ | |||
private readonly IHttpClient _apiClient; | |||
private readonly HttpClient _apiClient; | |||
private readonly ILogger<BasketService> _logger; | |||
private readonly UrlsConfig _urls; | |||
private readonly IHttpContextAccessor _httpContextAccessor; | |||
public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger<BasketService> logger, IOptionsSnapshot<UrlsConfig> config) | |||
public BasketService(HttpClient httpClient,ILogger<BasketService> logger, IOptions<UrlsConfig> config) | |||
{ | |||
_apiClient = httpClient; | |||
_logger = logger; | |||
_urls = config.Value; | |||
_httpContextAccessor = httpContextAccessor; | |||
} | |||
public async Task<BasketData> GetById(string id) | |||
{ | |||
var token = await GetUserTokenAsync(); | |||
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id), token); | |||
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id)); | |||
var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null; | |||
return basket; | |||
} | |||
public async Task Update(BasketData currentBasket) | |||
{ | |||
var token = await GetUserTokenAsync(); | |||
var data = await _apiClient.PostAsync<BasketData>(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), currentBasket, token); | |||
int i = 0; | |||
} | |||
var basketContent = new StringContent(JsonConvert.SerializeObject(currentBasket), System.Text.Encoding.UTF8, "application/json"); | |||
async Task<string> GetUserTokenAsync() | |||
{ | |||
var context = _httpContextAccessor.HttpContext; | |||
return await context.GetTokenAsync("access_token"); | |||
var data = await _apiClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), basketContent); | |||
} | |||
} | |||
} |
@ -1,43 +1,42 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; | |||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||
using Microsoft.Extensions.Logging; | |||
using Microsoft.Extensions.Options; | |||
using Newtonsoft.Json; | |||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; | |||
using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; | |||
using System.Collections.Generic; | |||
using System.Net.Http; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services | |||
{ | |||
public class CatalogService : ICatalogService | |||
{ | |||
private readonly IHttpClient _apiClient; | |||
private readonly HttpClient _httpClient; | |||
private readonly ILogger<CatalogService> _logger; | |||
private readonly UrlsConfig _urls; | |||
public CatalogService(IHttpClient httpClient, ILogger<CatalogService> logger, IOptionsSnapshot<UrlsConfig> config) | |||
public CatalogService(HttpClient httpClient, ILogger<CatalogService> logger, IOptions<UrlsConfig> config) | |||
{ | |||
_apiClient = httpClient; | |||
_httpClient = httpClient; | |||
_logger = logger; | |||
_urls = config.Value; | |||
} | |||
public async Task<CatalogItem> GetCatalogItem(int id) | |||
{ | |||
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); | |||
var item = JsonConvert.DeserializeObject<CatalogItem>(data); | |||
return item; | |||
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); | |||
var catalogItem = JsonConvert.DeserializeObject<CatalogItem>(stringContent); | |||
return catalogItem; | |||
} | |||
public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids) | |||
{ | |||
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); | |||
var item = JsonConvert.DeserializeObject<CatalogItem[]>(data); | |||
return item; | |||
var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); | |||
var catalogItems = JsonConvert.DeserializeObject<CatalogItem[]>(stringContent); | |||
return catalogItems; | |||
} | |||
} | |||
} |
@ -1,19 +1,19 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus</RootNamespace> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus</RootNamespace> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac" Version="4.6.2" /> | |||
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="2.0.0" /> | |||
<PackageReference Include="Microsoft.CSharp" Version="4.4.1" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac" Version="4.2.1" /> | |||
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="3.0.2" /> | |||
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\EventBus\EventBus.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\EventBus\EventBus.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,19 +1,19 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.0.1" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.1" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,20 +1,20 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="..\common\Guard.cs" Link="Internal\Guard.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" /> | |||
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" /> | |||
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Thread" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.4.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" /> | |||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> | |||
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Thread" Version="4.3.0" /> | |||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.0" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,16 +0,0 @@ | |||
using System.Net.Http; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http | |||
{ | |||
public interface IHttpClient | |||
{ | |||
Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer"); | |||
Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer"); | |||
Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer"); | |||
Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer"); | |||
} | |||
} |
@ -1,15 +0,0 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http</RootNamespace> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.0.1" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" /> | |||
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" /> | |||
<PackageReference Include="Polly" Version="5.8.0" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,191 +0,0 @@ | |||
using Microsoft.Extensions.Logging; | |||
using Newtonsoft.Json; | |||
using Polly; | |||
using Polly.Wrap; | |||
using System; | |||
using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.Http; | |||
using System.Net.Http.Headers; | |||
using System.Threading.Tasks; | |||
using Microsoft.AspNetCore.Http; | |||
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http | |||
{ | |||
/// <summary> | |||
/// HttpClient wrapper that integrates Retry and Circuit | |||
/// breaker policies when invoking HTTP services. | |||
/// Based on Polly library: https://github.com/App-vNext/Polly | |||
/// </summary> | |||
public class ResilientHttpClient : IHttpClient | |||
{ | |||
private readonly HttpClient _client; | |||
private readonly ILogger<ResilientHttpClient> _logger; | |||
private readonly Func<string, IEnumerable<Policy>> _policyCreator; | |||
private ConcurrentDictionary<string, PolicyWrap> _policyWrappers; | |||
private readonly IHttpContextAccessor _httpContextAccessor; | |||
public ResilientHttpClient(Func<string, IEnumerable<Policy>> policyCreator, ILogger<ResilientHttpClient> logger, IHttpContextAccessor httpContextAccessor) | |||
{ | |||
_client = new HttpClient(); | |||
_logger = logger; | |||
_policyCreator = policyCreator; | |||
_policyWrappers = new ConcurrentDictionary<string, PolicyWrap>(); | |||
_httpContextAccessor = httpContextAccessor; | |||
} | |||
public Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer") | |||
{ | |||
return DoPostPutAsync(HttpMethod.Post, uri, item, authorizationToken, requestId, authorizationMethod); | |||
} | |||
public Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer") | |||
{ | |||
return DoPostPutAsync(HttpMethod.Put, uri, item, authorizationToken, requestId, authorizationMethod); | |||
} | |||
public Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer") | |||
{ | |||
var origin = GetOriginFromUri(uri); | |||
return HttpInvoker(origin, async () => | |||
{ | |||
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, uri); | |||
SetAuthorizationHeader(requestMessage); | |||
if (authorizationToken != null) | |||
{ | |||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken); | |||
} | |||
if (requestId != null) | |||
{ | |||
requestMessage.Headers.Add("x-requestid", requestId); | |||
} | |||
return await _client.SendAsync(requestMessage); | |||
}); | |||
} | |||
public Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer") | |||
{ | |||
var origin = GetOriginFromUri(uri); | |||
return HttpInvoker(origin, async () => | |||
{ | |||
var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri); | |||
SetAuthorizationHeader(requestMessage); | |||
if (authorizationToken != null) | |||
{ | |||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken); | |||
} | |||
var response = await _client.SendAsync(requestMessage); | |||
// raise exception if HttpResponseCode 500 | |||
// needed for circuit breaker to track fails | |||
if (response.StatusCode == HttpStatusCode.InternalServerError) | |||
{ | |||
throw new HttpRequestException(); | |||
} | |||
if (!response.IsSuccessStatusCode) | |||
{ | |||
return null; | |||
} | |||
return await response.Content.ReadAsStringAsync(); | |||
}); | |||
} | |||
private Task<HttpResponseMessage> DoPostPutAsync<T>(HttpMethod method, string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer") | |||
{ | |||
if (method != HttpMethod.Post && method != HttpMethod.Put) | |||
{ | |||
throw new ArgumentException("Value must be either post or put.", nameof(method)); | |||
} | |||
// a new StringContent must be created for each retry | |||
// as it is disposed after each call | |||
var origin = GetOriginFromUri(uri); | |||
return HttpInvoker(origin, async () => | |||
{ | |||
var requestMessage = new HttpRequestMessage(method, uri); | |||
SetAuthorizationHeader(requestMessage); | |||
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json"); | |||
if (authorizationToken != null) | |||
{ | |||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken); | |||
} | |||
if (requestId != null) | |||
{ | |||
requestMessage.Headers.Add("x-requestid", requestId); | |||
} | |||
var response = await _client.SendAsync(requestMessage); | |||
// raise exception if HttpResponseCode 500 | |||
// needed for circuit breaker to track fails | |||
if (response.StatusCode == HttpStatusCode.InternalServerError) | |||
{ | |||
throw new HttpRequestException(); | |||
} | |||
return response; | |||
}); | |||
} | |||
private async Task<T> HttpInvoker<T>(string origin, Func<Task<T>> action) | |||
{ | |||
var normalizedOrigin = NormalizeOrigin(origin); | |||
if (!_policyWrappers.TryGetValue(normalizedOrigin, out PolicyWrap policyWrap)) | |||
{ | |||
policyWrap = Policy.WrapAsync(_policyCreator(normalizedOrigin).ToArray()); | |||
_policyWrappers.TryAdd(normalizedOrigin, policyWrap); | |||
} | |||
// Executes the action applying all | |||
// the policies defined in the wrapper | |||
return await policyWrap.ExecuteAsync(action, new Context(normalizedOrigin)); | |||
} | |||
private static string NormalizeOrigin(string origin) | |||
{ | |||
return origin?.Trim()?.ToLower(); | |||
} | |||
private static string GetOriginFromUri(string uri) | |||
{ | |||
var url = new Uri(uri); | |||
var origin = $"{url.Scheme}://{url.DnsSafeHost}:{url.Port}"; | |||
return origin; | |||
} | |||
private void SetAuthorizationHeader(HttpRequestMessage requestMessage) | |||
{ | |||
var authorizationHeader = _httpContextAccessor.HttpContext.Request.Headers["Authorization"]; | |||
if (!string.IsNullOrEmpty(authorizationHeader)) | |||
{ | |||
requestMessage.Headers.Add("Authorization", new List<string>() { authorizationHeader }); | |||
} | |||
} | |||
} | |||
} |
@ -1,125 +0,0 @@ | |||
using Microsoft.AspNetCore.Http; | |||
using Microsoft.Extensions.Logging; | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Net; | |||
using System.Net.Http; | |||
using System.Net.Http.Headers; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http | |||
{ | |||
public class StandardHttpClient : IHttpClient | |||
{ | |||
private HttpClient _client; | |||
private ILogger<StandardHttpClient> _logger; | |||
private readonly IHttpContextAccessor _httpContextAccessor; | |||
public StandardHttpClient(ILogger<StandardHttpClient> logger, IHttpContextAccessor httpContextAccessor) | |||
{ | |||
_client = new HttpClient(); | |||
_logger = logger; | |||
_httpContextAccessor = httpContextAccessor; | |||
} | |||
public async Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer") | |||
{ | |||
var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri); | |||
SetAuthorizationHeader(requestMessage); | |||
if (authorizationToken != null) | |||
{ | |||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken); | |||
} | |||
var response = await _client.SendAsync(requestMessage); | |||
if (!response.IsSuccessStatusCode) | |||
{ | |||
return null; | |||
} | |||
return await response.Content.ReadAsStringAsync(); | |||
} | |||
private async Task<HttpResponseMessage> DoPostPutAsync<T>(HttpMethod method, string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer") | |||
{ | |||
if (method != HttpMethod.Post && method != HttpMethod.Put) | |||
{ | |||
throw new ArgumentException("Value must be either post or put.", nameof(method)); | |||
} | |||
// a new StringContent must be created for each retry | |||
// as it is disposed after each call | |||
var requestMessage = new HttpRequestMessage(method, uri); | |||
SetAuthorizationHeader(requestMessage); | |||
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(item), System.Text.Encoding.UTF8, "application/json"); | |||
if (authorizationToken != null) | |||
{ | |||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken); | |||
} | |||
if (requestId != null) | |||
{ | |||
requestMessage.Headers.Add("x-requestid", requestId); | |||
} | |||
var response = await _client.SendAsync(requestMessage); | |||
// raise exception if HttpResponseCode 500 | |||
// needed for circuit breaker to track fails | |||
if (response.StatusCode == HttpStatusCode.InternalServerError) | |||
{ | |||
throw new HttpRequestException(); | |||
} | |||
return response; | |||
} | |||
public async Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer") | |||
{ | |||
return await DoPostPutAsync(HttpMethod.Post, uri, item, authorizationToken, requestId, authorizationMethod); | |||
} | |||
public async Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer") | |||
{ | |||
return await DoPostPutAsync(HttpMethod.Put, uri, item, authorizationToken, requestId, authorizationMethod); | |||
} | |||
public async Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer") | |||
{ | |||
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, uri); | |||
SetAuthorizationHeader(requestMessage); | |||
if (authorizationToken != null) | |||
{ | |||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken); | |||
} | |||
if (requestId != null) | |||
{ | |||
requestMessage.Headers.Add("x-requestid", requestId); | |||
} | |||
return await _client.SendAsync(requestMessage); | |||
} | |||
private void SetAuthorizationHeader(HttpRequestMessage requestMessage) | |||
{ | |||
var authorizationHeader = _httpContextAccessor.HttpContext.Request.Headers["Authorization"]; | |||
if (!string.IsNullOrEmpty(authorizationHeader)) | |||
{ | |||
requestMessage.Headers.Add("Authorization", new List<string>() { authorizationHeader }); | |||
} | |||
} | |||
} | |||
} | |||
@ -1,12 +1,12 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||
<TargetFramework>netcoreapp2.1</TargetFramework> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Polly" Version="5.8.0" /> | |||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> | |||
<PackageReference Include="Polly" Version="6.0.1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.1" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,33 +1,35 @@ | |||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.1</TargetFramework> | |||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Content Update="web.config"> | |||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | |||
</Content> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Content Update="web.config"> | |||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | |||
</Content> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> | |||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> | |||
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.2.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.2" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.4.0" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.7.2" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.App" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.1" /> | |||
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.6" /> | |||
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,3 +1,4 @@ | |||
{ | |||
"directory": "wwwroot/lib" | |||
"directory": "wwwroot/lib", | |||
"registry": "https://registry.bower.io" | |||
} |
@ -1,9 +1,9 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Identity.API | |||
{ | |||
public class AppSettings | |||
{ | |||
public string MvcClient { get; set; } | |||
public class AppSettings | |||
{ | |||
public string MvcClient { get; set; } | |||
public bool UseCustomizationData { get; set; } | |||
} | |||
public bool UseCustomizationData { get; set; } | |||
} | |||
} |
@ -1,62 +1,59 @@ | |||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion> | |||
<UserSecretsId>aspnet-eShopOnContainers.Identity-90487118-103c-4ff0-b9da-e5e26f7ab0c5</UserSecretsId> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Content Include="Setup\**\*;"> | |||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | |||
</Content> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> | |||
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="2.1.0" /> | |||
<PackageReference Include="IdentityServer4.EntityFramework" Version="2.1.1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.1" /> | |||
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.2.0" /> | |||
</ItemGroup> | |||
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish"> | |||
<Exec Command="bower install --allow-root" /> | |||
<Exec Command="dotnet bundle" Condition="'$(ASPNETCORE_ENVIRONMENT)'!='Development'" /> | |||
</Target> | |||
<ItemGroup> | |||
<DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.5.357" /> | |||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" /> | |||
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" /> | |||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<EmbeddedResource Include="Certificate\idsrv3test.pfx" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\WebHostCustomization\WebHost.Customization\WebHost.Customization.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Update="Setup\*"> | |||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |||
</None> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Folder Include="Extensions\" /> | |||
</ItemGroup> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.1</TargetFramework> | |||
<UserSecretsId>aspnet-eShopOnContainers.Identity-90487118-103c-4ff0-b9da-e5e26f7ab0c5</UserSecretsId> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Content Include="Setup\**\*;"> | |||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | |||
</Content> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.2" /> | |||
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="2.1.0" /> | |||
<PackageReference Include="IdentityServer4.EntityFramework" Version="2.1.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.4.0" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.7.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.App" /> | |||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="0.3.3" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.1" /> | |||
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" /> | |||
</ItemGroup> | |||
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish"> | |||
<Exec Command="bower install --allow-root" /> | |||
<Exec Command="dotnet bundle" Condition="'$(ASPNETCORE_ENVIRONMENT)'!='Development'" /> | |||
</Target> | |||
<ItemGroup> | |||
<DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.7.385" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<EmbeddedResource Include="Certificate\idsrv3test.pfx" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\WebHostCustomization\WebHost.Customization\WebHost.Customization.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Update="Setup\*"> | |||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |||
</None> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Folder Include="Extensions\" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,19 +1,20 @@ | |||
{ | |||
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word;", | |||
"IsClusterEnv": "False", | |||
"MvcClient": "http://localhost:5100", | |||
"SpaClient": "http://localhost:5104", | |||
"XamarinCallback": "http://localhost:5105/xamarincallback", | |||
"UseCustomizationData": false, | |||
"Logging": { | |||
"IncludeScopes": false, | |||
"LogLevel": { | |||
"Default": "Trace", | |||
"System": "Information", | |||
"Microsoft": "Information" | |||
} | |||
}, | |||
"ApplicationInsights": { | |||
"InstrumentationKey": "" | |||
} | |||
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word;", | |||
"IsClusterEnv": "False", | |||
"IdentityServer": "http://localhost:5105", | |||
"MvcClient": "http://localhost:5100", | |||
"SpaClient": "http://localhost:5104", | |||
"XamarinCallback": "http://localhost:5105/xamarincallback", | |||
"UseCustomizationData": false, | |||
"Logging": { | |||
"IncludeScopes": false, | |||
"LogLevel": { | |||
"Default": "Trace", | |||
"System": "Information", | |||
"Microsoft": "Information" | |||
} | |||
}, | |||
"ApplicationInsights": { | |||
"InstrumentationKey": "" | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
{ | |||
"name": "asp.net", | |||
"version": "1.0.0", | |||
"lockfileVersion": 1, | |||
"requires": true, | |||
"dependencies": { | |||
"bower": { | |||
"version": "1.8.4", | |||
"resolved": "https://registry.npmjs.org/bower/-/bower-1.8.4.tgz", | |||
"integrity": "sha1-54dqB23rgTf30GUl3F6MZtuC8oo=", | |||
"dev": true | |||
} | |||
} | |||
} |
@ -0,0 +1,8 @@ | |||
{ | |||
"version": "1.0.0", | |||
"name": "asp.net", | |||
"private": true, | |||
"devDependencies": { | |||
"bower": "^1.8.4" | |||
} | |||
} |
@ -1,56 +1,54 @@ | |||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
<RootNamespace>Microsoft.eShopOnContainers.Services.Marketing.API</RootNamespace> | |||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> | |||
<UserSecretsId>aspnet-Marketing.API-20161122013619</UserSecretsId> | |||
<AssemblyName /> | |||
<ApplicationInsightsResourceId>/subscriptions/6c22bb55-0221-4ce4-9bf1-3c4a10a7294c/resourcegroups/eshop-log/providers/microsoft.insights/components/eshopappinsights</ApplicationInsightsResourceId> | |||
<ApplicationInsightsAnnotationResourceId>/subscriptions/6c22bb55-0221-4ce4-9bf1-3c4a10a7294c/resourcegroups/eshop-log/providers/microsoft.insights/components/eshopappinsights</ApplicationInsightsAnnotationResourceId> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.1</TargetFramework> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
<RootNamespace>Microsoft.eShopOnContainers.Services.Marketing.API</RootNamespace> | |||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> | |||
<UserSecretsId>aspnet-Marketing.API-20161122013619</UserSecretsId> | |||
<AssemblyName /> | |||
<ApplicationInsightsResourceId>/subscriptions/6c22bb55-0221-4ce4-9bf1-3c4a10a7294c/resourcegroups/eshop-log/providers/microsoft.insights/components/eshopappinsights</ApplicationInsightsResourceId> | |||
<ApplicationInsightsAnnotationResourceId>/subscriptions/6c22bb55-0221-4ce4-9bf1-3c4a10a7294c/resourcegroups/eshop-log/providers/microsoft.insights/components/eshopappinsights</ApplicationInsightsAnnotationResourceId> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Folder Include="Connected Services\" /> | |||
<Folder Include="Infrastructure\MarketingMigrations\" /> | |||
<Content Include="Pics\**\*;"> | |||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | |||
</Content> | |||
<Folder Include="Infrastructure\MarketingMigrations\" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> | |||
<PackageReference Include="mongocsharpdriver" Version="2.5.0" /> | |||
<PackageReference Include="MongoDB.Bson" Version="2.5.0" /> | |||
<PackageReference Include="MongoDB.Driver" Version="2.5.0" /> | |||
<PackageReference Include="MongoDB.Driver.Core" Version="2.5.0" /> | |||
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.2.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.AzureStorage\Microsoft.Extensions.HealthChecks.AzureStorage.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\WebHostCustomization\WebHost.Customization\WebHost.Customization.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Folder Include="Connected Services\" /> | |||
<Folder Include="Infrastructure\MarketingMigrations\" /> | |||
<Content Include="Pics\**\*;"> | |||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | |||
</Content> | |||
<Folder Include="Infrastructure\MarketingMigrations\" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.2" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.4.0" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.7.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.App" /> | |||
<PackageReference Include="mongocsharpdriver" Version="2.5.0" /> | |||
<PackageReference Include="MongoDB.Bson" Version="2.5.0" /> | |||
<PackageReference Include="MongoDB.Driver" Version="2.5.0" /> | |||
<PackageReference Include="MongoDB.Driver.Core" Version="2.5.0" /> | |||
<PackageReference Include="Swashbuckle.AspNetCore" Version="2.4.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.AzureStorage\Microsoft.Extensions.HealthChecks.AzureStorage.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\WebHostCustomization\WebHost.Customization\WebHost.Customization.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Update="Pics\*"> | |||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |||
</None> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Update="Pics\*"> | |||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |||
</None> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<WCFMetadata Include="Connected Services" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<WCFMetadata Include="Connected Services" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,22 +1,21 @@ | |||
{ | |||
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", | |||
"IdentityUrl": "http://localhost:5105", | |||
"UseCustomizationData": false, | |||
"Logging": { | |||
"IncludeScopes": false, | |||
"LogLevel": { | |||
"Default": "Trace", | |||
"System": "Information", | |||
"Microsoft": "Information" | |||
} | |||
}, | |||
"AzureServiceBusEnabled": false, | |||
"SubscriptionClientName": "Ordering", | |||
"GracePeriodTime": "1", | |||
"CheckUpdateTime": "30000", | |||
"ApplicationInsights": { | |||
"InstrumentationKey": "" | |||
}, | |||
"EventBusRetryCount": 5, | |||
"EventBusConnection": "localhost" | |||
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", | |||
"IdentityUrl": "http://localhost:5105", | |||
"UseCustomizationData": false, | |||
"Logging": { | |||
"IncludeScopes": false, | |||
"LogLevel": { | |||
"Default": "Trace", | |||
"System": "Information", | |||
"Microsoft": "Information" | |||
} | |||
}, | |||
"AzureServiceBusEnabled": false, | |||
"SubscriptionClientName": "Ordering", | |||
"CheckUpdateTime": "30000", | |||
"ApplicationInsights": { | |||
"InstrumentationKey": "" | |||
}, | |||
"EventBusRetryCount": 5, | |||
"EventBusConnection": "localhost" | |||
} |
@ -1,26 +1,31 @@ | |||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.1</TargetFramework> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Folder Include="wwwroot\" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Folder Include="wwwroot\" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" /> | |||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0-preview2-final" /> | |||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.0.0-preview2-final" /> | |||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Redis" Version="1.0.0-preview2-final" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.2" /> | |||
<PackageReference Include="Microsoft.AspNetCore.App" /> | |||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.2" /> | |||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.0.2" /> | |||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Redis" Version="1.0.2" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.4.0" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.7.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.1" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,30 +1,28 @@ | |||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp2.1</TargetFramework> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.5.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta6" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.0.1-beta1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.1" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.2" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.4.0" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.7.1" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.0-beta8" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.1.1" /> | |||
<PackageReference Include="Microsoft.AspNetCore.App" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.1.1" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" /> | |||
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@ -1,3 +1,4 @@ | |||
{ | |||
"directory": "wwwroot/lib" | |||
"directory": "wwwroot/lib", | |||
"registry": "https://registry.bower.io" | |||
} |
@ -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); | |||
} | |||
} | |||
} |
@ -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); | |||
} | |||
} | |||
} |
@ -1,10 +0,0 @@ | |||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||
using System; | |||
namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure | |||
{ | |||
public interface IResilientHttpClientFactory | |||
{ | |||
ResilientHttpClient CreateResilientHttpClient(); | |||
} | |||
} |
@ -1,66 +0,0 @@ | |||
using Microsoft.AspNetCore.Http; | |||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||
using Microsoft.Extensions.Logging; | |||
using Polly; | |||
using System; | |||
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 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); | |||
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.ExecutionKey}, " + | |||
$"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"); | |||
}) | |||
}; | |||
} | |||
} |
@ -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,214 +1,289 @@ | |||
using Microsoft.ApplicationInsights.Extensibility; | |||
using Microsoft.ApplicationInsights.ServiceFabric; | |||
using Microsoft.AspNetCore.Authentication.Cookies; | |||
using Microsoft.AspNetCore.Authentication.JwtBearer; | |||
using Microsoft.AspNetCore.Authentication.OpenIdConnect; | |||
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; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.HealthChecks; | |||
using Microsoft.Extensions.Logging; | |||
using Polly; | |||
using Polly.Extensions.Http; | |||
using StackExchange.Redis; | |||
using System; | |||
using System.IdentityModel.Tokens.Jwt; | |||
using System.Net.Http; | |||
using WebMVC.Infrastructure; | |||
using WebMVC.Infrastructure.Middlewares; | |||
using WebMVC.Services; | |||
namespace Microsoft.eShopOnContainers.WebMVC | |||
{ | |||
public class Startup | |||
{ | |||
public Startup(IConfiguration configuration) | |||
{ | |||
Configuration = configuration; | |||
} | |||
public IConfiguration Configuration { get; } | |||
// This method gets called by the runtime. Use this method to add services to the container. | |||
public void ConfigureServices(IServiceCollection services) | |||
{ | |||
RegisterAppInsights(services); | |||
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); | |||
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. | |||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |||
{ | |||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); | |||
loggerFactory.AddConsole(Configuration.GetSection("Logging")); | |||
loggerFactory.AddDebug(); | |||
loggerFactory.AddAzureWebAppDiagnostics(); | |||
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); | |||
if (env.IsDevelopment()) | |||
{ | |||
app.UseDeveloperExceptionPage(); | |||
app.UseBrowserLink(); | |||
} | |||
else | |||
{ | |||
app.UseExceptionHandler("/Error"); | |||
} | |||
var pathBase = Configuration["PATH_BASE"]; | |||
if (!string.IsNullOrEmpty(pathBase)) | |||
{ | |||
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); | |||
app.UsePathBase(pathBase); | |||
} | |||
public class Startup | |||
{ | |||
public Startup(IConfiguration configuration) | |||
{ | |||
Configuration = configuration; | |||
} | |||
public IConfiguration Configuration { get; } | |||
// This method gets called by the runtime. Use this method to add services to the IoC container. | |||
public void ConfigureServices(IServiceCollection services) | |||
{ | |||
services.Configure<CookiePolicyOptions>(options => | |||
{ | |||
// This lambda determines whether user consent for non-essential cookies is needed for a given request. | |||
options.CheckConsentNeeded = context => true; | |||
options.MinimumSameSitePolicy = SameSiteMode.None; | |||
}); | |||
services.AddAppInsight(Configuration) | |||
.AddHealthChecks(Configuration) | |||
.AddCustomMvc(Configuration) | |||
.AddHttpClientServices(Configuration) | |||
.AddHttpClientLogging(Configuration) //Opt-in HttpClientLogging config | |||
.AddCustomAuthentication(Configuration); | |||
} | |||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |||
{ | |||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); | |||
loggerFactory.AddAzureWebAppDiagnostics(); | |||
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); | |||
if (env.IsDevelopment()) | |||
{ | |||
app.UseDeveloperExceptionPage(); | |||
} | |||
else | |||
{ | |||
app.UseExceptionHandler("/Error"); | |||
} | |||
app.UseCookiePolicy(); | |||
var pathBase = Configuration["PATH_BASE"]; | |||
if (!string.IsNullOrEmpty(pathBase)) | |||
{ | |||
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); | |||
app.UsePathBase(pathBase); | |||
} | |||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously | |||
app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); | |||
app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200)); | |||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously | |||
app.UseSession(); | |||
app.UseStaticFiles(); | |||
if (Configuration.GetValue<bool>("UseLoadTest")) | |||
{ | |||
app.UseMiddleware<ByPassAuthMiddleware>(); | |||
} | |||
app.UseAuthentication(); | |||
var log = loggerFactory.CreateLogger("identity"); | |||
WebContextSeed.Seed(app, env, loggerFactory); | |||
app.UseMvc(routes => | |||
{ | |||
routes.MapRoute( | |||
name: "default", | |||
template: "{controller=Catalog}/{action=Index}/{id?}"); | |||
routes.MapRoute( | |||
name: "defaultError", | |||
template: "{controller=Error}/{action=Error}"); | |||
}); | |||
} | |||
private void RegisterAppInsights(IServiceCollection services) | |||
{ | |||
services.AddApplicationInsightsTelemetry(Configuration); | |||
var orchestratorType = Configuration.GetValue<string>("OrchestratorType"); | |||
if (orchestratorType?.ToUpper() == "K8S") | |||
{ | |||
// Enable K8s telemetry initializer | |||
services.EnableKubernetes(); | |||
} | |||
if (orchestratorType?.ToUpper() == "SF") | |||
{ | |||
// Enable SF telemetry initializer | |||
services.AddSingleton<ITelemetryInitializer>((serviceProvider) => | |||
new FabricTelemetryInitializer()); | |||
} | |||
} | |||
} | |||
app.UseSession(); | |||
app.UseStaticFiles(); | |||
if (Configuration.GetValue<bool>("UseLoadTest")) | |||
{ | |||
app.UseMiddleware<ByPassAuthMiddleware>(); | |||
} | |||
app.UseAuthentication(); | |||
var log = loggerFactory.CreateLogger("identity"); | |||
WebContextSeed.Seed(app, env, loggerFactory); | |||
app.UseMvc(routes => | |||
{ | |||
routes.MapRoute( | |||
name: "default", | |||
template: "{controller=Catalog}/{action=Index}/{id?}"); | |||
routes.MapRoute( | |||
name: "defaultError", | |||
template: "{controller=Error}/{action=Error}"); | |||
}); | |||
} | |||
} | |||
static class ServiceCollectionExtensions | |||
{ | |||
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddApplicationInsightsTelemetry(configuration); | |||
var orchestratorType = configuration.GetValue<string>("OrchestratorType"); | |||
if (orchestratorType?.ToUpper() == "K8S") | |||
{ | |||
// Enable K8s telemetry initializer | |||
services.EnableKubernetes(); | |||
} | |||
if (orchestratorType?.ToUpper() == "SF") | |||
{ | |||
// Enable SF telemetry initializer | |||
services.AddSingleton<ITelemetryInitializer>((serviceProvider) => | |||
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.AddOptions(); | |||
services.Configure<AppSettings>(configuration); | |||
services | |||
.AddMvc() | |||
.SetCompatibilityVersion(AspNetCore.Mvc.CompatibilityVersion.Version_2_1); | |||
services.AddSession(); | |||
if (configuration.GetValue<string>("IsClusterEnv") == bool.TrueString) | |||
{ | |||
services.AddDataProtection(opts => | |||
{ | |||
opts.ApplicationDiscriminator = "eshop.webmvc"; | |||
}) | |||
.PersistKeysToRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys"); | |||
} | |||
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) | |||
{ | |||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||
//register delegating handlers | |||
services.AddTransient<HttpClientAuthorizationDelegatingHandler>(); | |||
services.AddTransient<HttpClientRequestIdDelegatingHandler>(); | |||
//set 5 min as the lifetime for each HttpMessageHandler int the pool | |||
services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(TimeSpan.FromMinutes(5)); | |||
//add http client services | |||
services.AddHttpClient<IBasketService, BasketService>() | |||
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes | |||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() | |||
.AddPolicyHandler(GetRetryPolicy()) | |||
.AddPolicyHandler(GetCircuitBreakerPolicy()); | |||
services.AddHttpClient<ICatalogService, CatalogService>() | |||
.AddPolicyHandler(GetRetryPolicy()) | |||
.AddPolicyHandler(GetCircuitBreakerPolicy()); | |||
services.AddHttpClient<IOrderingService, OrderingService>() | |||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() | |||
.AddHttpMessageHandler<HttpClientRequestIdDelegatingHandler>() | |||
.AddPolicyHandler(GetRetryPolicy()) | |||
.AddPolicyHandler(GetCircuitBreakerPolicy()); | |||
services.AddHttpClient<ICampaignService, CampaignService>() | |||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() | |||
.AddPolicyHandler(GetRetryPolicy()) | |||
.AddPolicyHandler(GetCircuitBreakerPolicy()); | |||
services.AddHttpClient<ILocationService, LocationService>() | |||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() | |||
.AddPolicyHandler(GetRetryPolicy()) | |||
.AddPolicyHandler(GetCircuitBreakerPolicy()); | |||
//add custom application services | |||
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>(); | |||
return services; | |||
} | |||
public static IServiceCollection AddHttpClientLogging(this IServiceCollection services, IConfiguration configuration) | |||
{ | |||
services.AddLogging(b => | |||
{ | |||
b.AddFilter((category, level) => true); // Spam the world with logs. | |||
// Add console logger so we can see all the logging produced by the client by default. | |||
b.AddConsole(c => c.IncludeScopes = true); | |||
// Add console logger | |||
b.AddDebug(); | |||
}); | |||
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.DefaultAuthenticateScheme = JwtBearerDefaults.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; | |||
} | |||
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() | |||
=> HttpPolicyExtensions | |||
.HandleTransientHttpError() | |||
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) | |||
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); | |||
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy() | |||
=> HttpPolicyExtensions | |||
.HandleTransientHttpError() | |||
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); | |||
} | |||
} |