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
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public class Startup
{
public Startup(IConfiguration 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.
public void ConfigureServices(IServiceCollection services)
{
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 add services to the IoC container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(opts =>
{
opts.MinimumSameSitePolicy = SameSiteMode.None;
opts.CheckConsentNeeded = context => true;
});
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.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
// 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);
loggerFactory.AddAzureWebAppDiagnostics();
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
app.UsePathBase(pathBase);
}
app.UseHttpsRedirection();
app.UseCookiePolicy();
string pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
app.UsePathBase(pathBase);
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200));
app.Map("/liveness", lapp => lapp.Run(async ctx => ctx.Response.StatusCode = 200));
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
app.UseSession();
app.UseStaticFiles();
app.UseSession();
app.UseStaticFiles();
if (Configuration.GetValue<bool>("UseLoadTest"))
{
app.UseMiddleware<ByPassAuthMiddleware>();
}
if (Configuration.GetValue<bool>("UseLoadTest"))
{
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 =>
{
routes.MapRoute(
name: "default",
template: "{controller=Catalog}/{action=Index}/{id?}");
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Catalog}/{action=Index}/{id?}");
routes.MapRoute(
name: "defaultError",
template: "{controller=Error}/{action=Error}");
});
}
}
routes.MapRoute(
name: "defaultError",
template: "{controller=Error}/{action=Error}");
});
}
}
static class ServiceCollectionExtensions
{
static class ServiceCollectionExtensions
{
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration)
{
services.AddApplicationInsightsTelemetry(configuration);
var orchestratorType = configuration.GetValue<string>("OrchestratorType");
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration)
{
services.AddApplicationInsightsTelemetry(configuration);
string orchestratorType = configuration.GetValue<string>("OrchestratorType");
if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
services.EnableKubernetes();
}
if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
services.EnableKubernetes();
}
if (orchestratorType?.ToUpper() == "SF")
{
// Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer());
}
if (orchestratorType?.ToUpper() == "SF")
{
// Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer());
}
return services;
}
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;
}
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration)
{
services.AddHealthChecks(checks =>
{
int minutes = 1;
if (int.TryParse(configuration["HealthCheck:Timeout"], out int 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));
});
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;
}
return services;
}
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure<AppSettings>(configuration);
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
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)
{
services.AddDataProtection(opts =>
{
opts.ApplicationDiscriminator = "eshop.webmvc";
})
.PersistKeysToRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys");
}
return services;
}
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>();
// 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>();
//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));
//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());
//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<ICatalogService, CatalogService>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<IOrderingService, OrderingService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddHttpMessageHandler<HttpClientRequestIdDelegatingHandler>()
.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<ICampaignService, CampaignService>()
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>()
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<ILocationService, LocationService>()
.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>();
//add custom application services
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
return services;
}
return services;
}
public static IServiceCollection AddHttpClientLogging(this IServiceCollection services, IConfiguration configuration)
{
services.AddLogging(b =>
{
b.AddFilter((category, level) => true); // Spam the world with logs.
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 so we can see all the logging produced by the client by default.
b.AddConsole(c => c.IncludeScopes = true);
// Add console logger
b.AddDebug();
});
// Add console logger
b.AddDebug();
});
return services;
}
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");
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
{
bool useLoadTest = configuration.GetValue<bool>("UseLoadTest");
string identityUrl = configuration.GetValue<string>("IdentityUrl");
string callBackUrl = configuration.GetValue<string>("CallBackUrl");
// Add Authentication services
// 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");
});
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = identityUrl.ToString();
options.SignedOutRedirectUri = callBackUrl.ToString();
options.ClientId = useLoadTest ? "mvctest" : "mvc";
options.ClientSecret = "secret";
options.ResponseType = useLoadTest ? "code id_token token" : "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.RequireHttpsMetadata = false;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("orders");
options.Scope.Add("basket");
options.Scope.Add("marketing");
options.Scope.Add("locations");
options.Scope.Add("webshoppingagg");
options.Scope.Add("orders.signalrhub");
});
return services;
}
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> 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));
}
}
}
static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
}
}
}