@ -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.Logging; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
using Newtonsoft.Json; | 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 | namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services | ||||
{ | { | ||||
public class BasketService : IBasketService | public class BasketService : IBasketService | ||||
{ | { | ||||
private readonly IHttpClient _apiClient; | |||||
private readonly HttpClient _httpClient; | |||||
private readonly ILogger<BasketService> _logger; | private readonly ILogger<BasketService> _logger; | ||||
private readonly UrlsConfig _urls; | 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; | _logger = logger; | ||||
_urls = config.Value; | _urls = config.Value; | ||||
_httpContextAccessor = httpContextAccessor; | |||||
} | } | ||||
public async Task<BasketData> GetById(string id) | 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; | var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null; | ||||
return basket; | return basket; | ||||
} | } | ||||
public async Task Update(BasketData currentBasket) | 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.Logging; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
using Newtonsoft.Json; | 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 | namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services | ||||
{ | { | ||||
public class CatalogService : ICatalogService | public class CatalogService : ICatalogService | ||||
{ | { | ||||
private readonly IHttpClient _apiClient; | |||||
private readonly HttpClient _httpClient; | |||||
private readonly ILogger<CatalogService> _logger; | private readonly ILogger<CatalogService> _logger; | ||||
private readonly UrlsConfig _urls; | 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; | _logger = logger; | ||||
_urls = config.Value; | _urls = config.Value; | ||||
} | } | ||||
public async Task<CatalogItem> GetCatalogItem(int id) | 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) | 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.Builder; | ||||
using Microsoft.AspNetCore.Hosting; | 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.Config; | ||||
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; | 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.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 Swashbuckle.AspNetCore.Swagger; | ||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IdentityModel.Tokens.Jwt; | |||||
using System.Net.Http; | |||||
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator | 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.Logging; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
using Newtonsoft.Json; | 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 | namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services | ||||
{ | { | ||||
public class BasketService : IBasketService | public class BasketService : IBasketService | ||||
{ | { | ||||
private readonly IHttpClient _apiClient; | |||||
private readonly HttpClient _apiClient; | |||||
private readonly ILogger<BasketService> _logger; | private readonly ILogger<BasketService> _logger; | ||||
private readonly UrlsConfig _urls; | 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; | _apiClient = httpClient; | ||||
_logger = logger; | _logger = logger; | ||||
_urls = config.Value; | _urls = config.Value; | ||||
_httpContextAccessor = httpContextAccessor; | |||||
} | } | ||||
public async Task<BasketData> GetById(string id) | 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; | var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject<BasketData>(data) : null; | ||||
return basket; | return basket; | ||||
} | } | ||||
public async Task Update(BasketData currentBasket) | 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.Logging; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
using Newtonsoft.Json; | 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 | namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services | ||||
{ | { | ||||
public class CatalogService : ICatalogService | public class CatalogService : ICatalogService | ||||
{ | { | ||||
private readonly IHttpClient _apiClient; | |||||
private readonly HttpClient _httpClient; | |||||
private readonly ILogger<CatalogService> _logger; | private readonly ILogger<CatalogService> _logger; | ||||
private readonly UrlsConfig _urls; | 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; | _logger = logger; | ||||
_urls = config.Value; | _urls = config.Value; | ||||
} | } | ||||
public async Task<CatalogItem> GetCatalogItem(int id) | 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) | 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"> | <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> | </Project> |
@ -1,19 +1,19 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <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> | </Project> |
@ -1,20 +1,20 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <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> | </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> | <PropertyGroup> | ||||
<TargetFramework>netcoreapp2.0</TargetFramework> | |||||
<TargetFramework>netcoreapp2.1</TargetFramework> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <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> | </ItemGroup> | ||||
</Project> | </Project> |
@ -1,33 +1,35 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | <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> | </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 | 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"> | <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> | </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"> | <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> | </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> | </Project> |
@ -1,30 +1,28 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | <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> | </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 | namespace Microsoft.eShopOnContainers.WebMVC.Services | ||||
{ | { | ||||
using global::WebMVC.Infrastructure; | using global::WebMVC.Infrastructure; | ||||
using AspNetCore.Authentication; | |||||
using AspNetCore.Http; | |||||
using BuildingBlocks.Resilience.Http; | |||||
using ViewModels; | |||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | |||||
using System.Net.Http; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using ViewModels; | |||||
public class CampaignService : ICampaignService | 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 ILogger<CampaignService> _logger; | ||||
private readonly string _remoteServiceBaseUrl; | 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; | _settings = settings; | ||||
_apiClient = httpClient; | |||||
_httpClient = httpClient; | |||||
_logger = logger; | _logger = logger; | ||||
_remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/m/campaigns/"; | _remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/m/campaigns/"; | ||||
_httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor)); | |||||
} | } | ||||
public async Task<Campaign> GetCampaigns(int pageSize, int pageIndex) | 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; | return response; | ||||
} | } | ||||
public async Task<CampaignItem> GetCampaignById(int id) | 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; | 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.Extensibility; | ||||
using Microsoft.ApplicationInsights.ServiceFabric; | using Microsoft.ApplicationInsights.ServiceFabric; | ||||
using Microsoft.AspNetCore.Authentication.Cookies; | using Microsoft.AspNetCore.Authentication.Cookies; | ||||
using Microsoft.AspNetCore.Authentication.JwtBearer; | |||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect; | using Microsoft.AspNetCore.Authentication.OpenIdConnect; | ||||
using Microsoft.AspNetCore.Builder; | using Microsoft.AspNetCore.Builder; | ||||
using Microsoft.AspNetCore.DataProtection; | using Microsoft.AspNetCore.DataProtection; | ||||
using Microsoft.AspNetCore.Hosting; | using Microsoft.AspNetCore.Hosting; | ||||
using Microsoft.AspNetCore.Http; | using Microsoft.AspNetCore.Http; | ||||
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | |||||
using Microsoft.eShopOnContainers.WebMVC.Infrastructure; | |||||
using Microsoft.eShopOnContainers.WebMVC.Services; | using Microsoft.eShopOnContainers.WebMVC.Services; | ||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | using Microsoft.eShopOnContainers.WebMVC.ViewModels; | ||||
using Microsoft.Extensions.Configuration; | using Microsoft.Extensions.Configuration; | ||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
using Microsoft.Extensions.HealthChecks; | using Microsoft.Extensions.HealthChecks; | ||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
using Polly; | |||||
using Polly.Extensions.Http; | |||||
using StackExchange.Redis; | using StackExchange.Redis; | ||||
using System; | using System; | ||||
using System.IdentityModel.Tokens.Jwt; | using System.IdentityModel.Tokens.Jwt; | ||||
using System.Net.Http; | |||||
using WebMVC.Infrastructure; | using WebMVC.Infrastructure; | ||||
using WebMVC.Infrastructure.Middlewares; | using WebMVC.Infrastructure.Middlewares; | ||||
using WebMVC.Services; | using WebMVC.Services; | ||||
namespace Microsoft.eShopOnContainers.WebMVC | 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 | #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 | #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)); | |||||
} | |||||
} | } |