1. Configured CookiePolicyOptions to the DI Container (Line 39) and used CookiePolicy MiddleWare (line 76) in order to support GDPR Cookie compliance

2. Set compatibility version to 2.1 according to documentation (line 172)
3. HTTPS Support
    a. Updated MVC pipeline to support HTTPS and specified port (Line 166-171)
    b. Configure HTTPS redirection options to the DI (line 51) and used HttpsRedirection MiddleWare (Line 75)
    c.
4. Used var instead of type (Line 100, 123, 145, 250-252)
This commit is contained in:
rafsanulhasan 2018-09-01 02:29:19 +06:00
parent 12b1be780e
commit e901aafc5c

View File

@ -24,258 +24,278 @@ using WebMVC.Services;
namespace Microsoft.eShopOnContainers.WebMVC namespace Microsoft.eShopOnContainers.WebMVC
{ {
public class Startup public class Startup
{ {
public Startup(IConfiguration configuration) public Startup(IConfiguration configuration)
{ {
Configuration = configuration; Configuration = configuration;
} }
public IConfiguration Configuration { get; } public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the IoC container. // This method gets called by the runtime. Use this method to add services to the IoC container.
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddAppInsight(Configuration) services.Configure<CookiePolicyOptions>(opts =>
.AddHealthChecks(Configuration) {
.AddCustomMvc(Configuration) opts.MinimumSameSitePolicy = SameSiteMode.None;
.AddHttpClientServices(Configuration) opts.CheckConsentNeeded = context => true;
//.AddHttpClientLogging(Configuration) //Opt-in HttpClientLogging config });
.AddCustomAuthentication(Configuration); services
} .AddAppInsight(Configuration)
.AddHealthChecks(Configuration)
.AddCustomMvc(Configuration)
.AddHttpClientServices(Configuration)
//.AddHttpClientLogging(Configuration) //Opt-in HttpClientLogging config
.AddCustomAuthentication(Configuration);
services.AddHttpsRedirection(options =>
{
options.HttpsPort = 4100;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ {
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
loggerFactory.AddAzureWebAppDiagnostics(); loggerFactory.AddAzureWebAppDiagnostics();
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
else else
{ {
app.UseExceptionHandler("/Error"); app.UseExceptionHandler("/Error");
} app.UseHsts();
}
var pathBase = Configuration["PATH_BASE"]; app.UseHttpsRedirection();
if (!string.IsNullOrEmpty(pathBase)) app.UseCookiePolicy();
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); string pathBase = Configuration["PATH_BASE"];
app.UsePathBase(pathBase); 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.UseSession();
app.UseStaticFiles(); app.UseStaticFiles();
if (Configuration.GetValue<bool>("UseLoadTest")) if (Configuration.GetValue<bool>("UseLoadTest"))
{ {
app.UseMiddleware<ByPassAuthMiddleware>(); app.UseMiddleware<ByPassAuthMiddleware>();
} }
app.UseAuthentication(); app.UseAuthentication();
var log = loggerFactory.CreateLogger("identity"); ILogger log = loggerFactory.CreateLogger("identity");
WebContextSeed.Seed(app, env, loggerFactory); WebContextSeed.Seed(app, env, loggerFactory);
app.UseMvc(routes => app.UseMvc(routes =>
{ {
routes.MapRoute( routes.MapRoute(
name: "default", name: "default",
template: "{controller=Catalog}/{action=Index}/{id?}"); template: "{controller=Catalog}/{action=Index}/{id?}");
routes.MapRoute( routes.MapRoute(
name: "defaultError", name: "defaultError",
template: "{controller=Error}/{action=Error}"); template: "{controller=Error}/{action=Error}");
}); });
} }
} }
static class ServiceCollectionExtensions static class ServiceCollectionExtensions
{ {
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration)
{ {
services.AddApplicationInsightsTelemetry(configuration); services.AddApplicationInsightsTelemetry(configuration);
var orchestratorType = configuration.GetValue<string>("OrchestratorType"); string orchestratorType = configuration.GetValue<string>("OrchestratorType");
if (orchestratorType?.ToUpper() == "K8S") if (orchestratorType?.ToUpper() == "K8S")
{ {
// Enable K8s telemetry initializer // Enable K8s telemetry initializer
services.EnableKubernetes(); services.EnableKubernetes();
} }
if (orchestratorType?.ToUpper() == "SF") if (orchestratorType?.ToUpper() == "SF")
{ {
// Enable SF telemetry initializer // Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) => services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer()); new FabricTelemetryInitializer());
} }
return services; return services;
} }
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration)
{ {
services.AddHealthChecks(checks => services.AddHealthChecks(checks =>
{ {
var minutes = 1; int minutes = 1;
if (int.TryParse(configuration["HealthCheck:Timeout"], out var minutesParsed)) if (int.TryParse(configuration["HealthCheck:Timeout"], out int minutesParsed))
{ {
minutes = minutesParsed; minutes = minutesParsed;
} }
checks.AddUrlCheck(configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes)); checks.AddUrlCheck(configuration["CatalogUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(configuration["OrderingUrlHC"], 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["BasketUrlHC"], TimeSpan.Zero); //No cache for this HealthCheck, better just for demos
checks.AddUrlCheck(configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes)); checks.AddUrlCheck(configuration["IdentityUrlHC"], TimeSpan.FromMinutes(minutes));
checks.AddUrlCheck(configuration["MarketingUrlHC"], TimeSpan.FromMinutes(minutes)); checks.AddUrlCheck(configuration["MarketingUrlHC"], TimeSpan.FromMinutes(minutes));
}); });
return services; return services;
} }
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{ {
services.AddOptions(); services.AddOptions();
services.Configure<AppSettings>(configuration); services.Configure<AppSettings>(configuration);
services.AddMvc(); services
.AddMvc(opts =>
{
opts.SslPort = 4100;
opts.RequireHttpsPermanent = true;
})
.SetCompatibilityVersion(AspNetCore.Mvc.CompatibilityVersion.Version_2_1);
services.AddSession(); services.AddSession();
if (configuration.GetValue<string>("IsClusterEnv") == bool.TrueString) if (configuration.GetValue<string>("IsClusterEnv") == bool.TrueString)
{ {
services.AddDataProtection(opts => services.AddDataProtection(opts =>
{ {
opts.ApplicationDiscriminator = "eshop.webmvc"; opts.ApplicationDiscriminator = "eshop.webmvc";
}) })
.PersistKeysToRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys"); .PersistKeysToRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys");
} }
return services; return services;
} }
// Adds all Http client services (like Service-Agents) using resilient Http requests based on HttpClient factory and Polly's policies // 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) public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration)
{ {
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//register delegating handlers //register delegating handlers
services.AddTransient<HttpClientAuthorizationDelegatingHandler>(); services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
services.AddTransient<HttpClientRequestIdDelegatingHandler>(); services.AddTransient<HttpClientRequestIdDelegatingHandler>();
//set 5 min as the lifetime for each HttpMessageHandler int the pool //set 5 min as the lifetime for each HttpMessageHandler int the pool
services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(TimeSpan.FromMinutes(5)); services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(TimeSpan.FromMinutes(5));
//add http client services //add http client services
services.AddHttpClient<IBasketService, BasketService>() services.AddHttpClient<IBasketService, BasketService>()
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes .SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() .AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy()) .AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy()); .AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<ICatalogService, CatalogService>() services.AddHttpClient<ICatalogService, CatalogService>()
.AddPolicyHandler(GetRetryPolicy()) .AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy()); .AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<IOrderingService, OrderingService>() services.AddHttpClient<IOrderingService, OrderingService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() .AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddHttpMessageHandler<HttpClientRequestIdDelegatingHandler>() .AddHttpMessageHandler<HttpClientRequestIdDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy()) .AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy()); .AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<ICampaignService, CampaignService>() services.AddHttpClient<ICampaignService, CampaignService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() .AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy()) .AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy()); .AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<ILocationService, LocationService>() services.AddHttpClient<ILocationService, LocationService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() .AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy()) .AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy()); .AddPolicyHandler(GetCircuitBreakerPolicy());
//add custom application services //add custom application services
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>(); services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
return services; return services;
} }
public static IServiceCollection AddHttpClientLogging(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddHttpClientLogging(this IServiceCollection services, IConfiguration configuration)
{ {
services.AddLogging(b => services.AddLogging(b =>
{ {
b.AddFilter((category, level) => true); // Spam the world with logs. 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. // Add console logger so we can see all the logging produced by the client by default.
b.AddConsole(c => c.IncludeScopes = true); b.AddConsole(c => c.IncludeScopes = true);
// Add console logger // Add console logger
b.AddDebug(); b.AddDebug();
}); });
return services; return services;
} }
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
{ {
var useLoadTest = configuration.GetValue<bool>("UseLoadTest"); bool useLoadTest = configuration.GetValue<bool>("UseLoadTest");
var identityUrl = configuration.GetValue<string>("IdentityUrl"); string identityUrl = configuration.GetValue<string>("IdentityUrl");
var callBackUrl = configuration.GetValue<string>("CallBackUrl"); string callBackUrl = configuration.GetValue<string>("CallBackUrl");
// Add Authentication services // Add Authentication services
services.AddAuthentication(options => services.AddAuthentication(options =>
{ {
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}) })
.AddCookie() .AddCookie()
.AddOpenIdConnect(options => .AddOpenIdConnect(options =>
{ {
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = identityUrl.ToString(); options.Authority = identityUrl.ToString();
options.SignedOutRedirectUri = callBackUrl.ToString(); options.SignedOutRedirectUri = callBackUrl.ToString();
options.ClientId = useLoadTest ? "mvctest" : "mvc"; options.ClientId = useLoadTest ? "mvctest" : "mvc";
options.ClientSecret = "secret"; options.ClientSecret = "secret";
options.ResponseType = useLoadTest ? "code id_token token" : "code id_token"; options.ResponseType = useLoadTest ? "code id_token token" : "code id_token";
options.SaveTokens = true; options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true; options.GetClaimsFromUserInfoEndpoint = true;
options.RequireHttpsMetadata = false; options.RequireHttpsMetadata = false;
options.Scope.Add("openid"); options.Scope.Add("openid");
options.Scope.Add("profile"); options.Scope.Add("profile");
options.Scope.Add("orders"); options.Scope.Add("orders");
options.Scope.Add("basket"); options.Scope.Add("basket");
options.Scope.Add("marketing"); options.Scope.Add("marketing");
options.Scope.Add("locations"); options.Scope.Add("locations");
options.Scope.Add("webshoppingagg"); options.Scope.Add("webshoppingagg");
options.Scope.Add("orders.signalrhub"); options.Scope.Add("orders.signalrhub");
}); });
return services; return services;
} }
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{ {
return HttpPolicyExtensions return HttpPolicyExtensions
.HandleTransientHttpError() .HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
} }
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy() static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{ {
return HttpPolicyExtensions return HttpPolicyExtensions
.HandleTransientHttpError() .HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)); .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
} }
} }
} }