|
@ -1,34 +1,62 @@ |
|
|
var configuration = GetConfiguration(); |
|
|
|
|
|
|
|
|
var builder = WebApplication.CreateBuilder(args); |
|
|
|
|
|
builder.Services.AddControllersWithViews(); |
|
|
|
|
|
|
|
|
Log.Logger = CreateSerilogLogger(configuration); |
|
|
|
|
|
|
|
|
AddApplicationInsights(builder); |
|
|
|
|
|
AddHealthChecks(builder); |
|
|
|
|
|
AddCustomMvc(builder); |
|
|
|
|
|
AddHttpClientServices(builder); |
|
|
|
|
|
AddCustomAuthentication(builder); |
|
|
|
|
|
|
|
|
try |
|
|
|
|
|
{ |
|
|
|
|
|
Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName); |
|
|
|
|
|
var host = BuildWebHost(configuration, args); |
|
|
|
|
|
|
|
|
builder.WebHost.CaptureStartupErrors(false); |
|
|
|
|
|
builder.Host.UseSerilog(CreateSerilogLogger(builder.Configuration)); |
|
|
|
|
|
|
|
|
Log.Information("Starting web host ({ApplicationContext})...", Program.AppName); |
|
|
|
|
|
host.Run(); |
|
|
|
|
|
|
|
|
var app = builder.Build(); |
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); |
|
|
|
|
|
if (app.Environment.IsDevelopment()) |
|
|
|
|
|
{ |
|
|
|
|
|
app.UseDeveloperExceptionPage(); |
|
|
} |
|
|
} |
|
|
catch (Exception ex) |
|
|
|
|
|
|
|
|
else |
|
|
{ |
|
|
{ |
|
|
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", Program.AppName); |
|
|
|
|
|
return 1; |
|
|
|
|
|
|
|
|
app.UseExceptionHandler("/Error"); |
|
|
} |
|
|
} |
|
|
finally |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var pathBase = builder.Configuration["PATH_BASE"]; |
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(pathBase)) |
|
|
{ |
|
|
{ |
|
|
Log.CloseAndFlush(); |
|
|
|
|
|
|
|
|
app.UsePathBase(pathBase); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
IWebHost BuildWebHost(IConfiguration configuration, string[] args) => |
|
|
|
|
|
WebHost.CreateDefaultBuilder(args) |
|
|
|
|
|
.CaptureStartupErrors(false) |
|
|
|
|
|
.ConfigureAppConfiguration(x => x.AddConfiguration(configuration)) |
|
|
|
|
|
.UseStartup<Startup>() |
|
|
|
|
|
.UseSerilog() |
|
|
|
|
|
.Build(); |
|
|
|
|
|
|
|
|
app.UseStaticFiles(); |
|
|
|
|
|
app.UseSession(); |
|
|
|
|
|
|
|
|
|
|
|
WebContextSeed.Seed(app, app.Environment); |
|
|
|
|
|
|
|
|
|
|
|
// Fix samesite issue when running eShop from docker-compose locally as by default http protocol is being used
|
|
|
|
|
|
// Refer to https://github.com/dotnet-architecture/eShopOnContainers/issues/1391
|
|
|
|
|
|
app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = SameSiteMode.Lax }); |
|
|
|
|
|
|
|
|
|
|
|
app.UseRouting(); |
|
|
|
|
|
|
|
|
|
|
|
app.UseAuthentication(); |
|
|
|
|
|
app.UseAuthorization(); |
|
|
|
|
|
|
|
|
|
|
|
app.MapControllerRoute("default", "{controller=Catalog}/{action=Index}/{id?}"); |
|
|
|
|
|
app.MapControllerRoute("defaultError", "{controller=Error}/{action=Error}"); |
|
|
|
|
|
app.MapControllers(); |
|
|
|
|
|
app.MapHealthChecks("/liveness", new HealthCheckOptions |
|
|
|
|
|
{ |
|
|
|
|
|
Predicate = r => r.Name.Contains("self") |
|
|
|
|
|
}); |
|
|
|
|
|
app.MapHealthChecks("/hc", new HealthCheckOptions() |
|
|
|
|
|
{ |
|
|
|
|
|
Predicate = _ => true, |
|
|
|
|
|
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
await app.RunAsync(); |
|
|
|
|
|
|
|
|
Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) |
|
|
Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) |
|
|
{ |
|
|
{ |
|
@ -36,7 +64,7 @@ Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) |
|
|
var logstashUrl = configuration["Serilog:LogstashgUrl"]; |
|
|
var logstashUrl = configuration["Serilog:LogstashgUrl"]; |
|
|
var cfg = new LoggerConfiguration() |
|
|
var cfg = new LoggerConfiguration() |
|
|
.ReadFrom.Configuration(configuration) |
|
|
.ReadFrom.Configuration(configuration) |
|
|
.Enrich.WithProperty("ApplicationContext", Program.AppName) |
|
|
|
|
|
|
|
|
.Enrich.WithProperty("ApplicationContext", AppName) |
|
|
.Enrich.FromLogContext() |
|
|
.Enrich.FromLogContext() |
|
|
.WriteTo.Console(); |
|
|
.WriteTo.Console(); |
|
|
if (!string.IsNullOrWhiteSpace(seqServerUrl)) |
|
|
if (!string.IsNullOrWhiteSpace(seqServerUrl)) |
|
@ -50,19 +78,100 @@ Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) |
|
|
return cfg.CreateLogger(); |
|
|
return cfg.CreateLogger(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
IConfiguration GetConfiguration() |
|
|
|
|
|
|
|
|
static void AddApplicationInsights(WebApplicationBuilder builder) |
|
|
|
|
|
{ |
|
|
|
|
|
builder.Services.AddApplicationInsightsTelemetry(builder.Configuration); |
|
|
|
|
|
builder.Services.AddApplicationInsightsKubernetesEnricher(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void AddHealthChecks(WebApplicationBuilder builder) |
|
|
|
|
|
{ |
|
|
|
|
|
builder.Services.AddHealthChecks() |
|
|
|
|
|
.AddCheck("self", () => HealthCheckResult.Healthy()) |
|
|
|
|
|
.AddUrlGroup(new Uri(builder.Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void AddCustomMvc(WebApplicationBuilder builder) |
|
|
{ |
|
|
{ |
|
|
var builder = new ConfigurationBuilder() |
|
|
|
|
|
.SetBasePath(Directory.GetCurrentDirectory()) |
|
|
|
|
|
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) |
|
|
|
|
|
.AddEnvironmentVariables(); |
|
|
|
|
|
|
|
|
builder.Services.AddOptions() |
|
|
|
|
|
.Configure<AppSettings>(builder.Configuration) |
|
|
|
|
|
.AddSession() |
|
|
|
|
|
.AddDistributedMemoryCache(); |
|
|
|
|
|
|
|
|
return builder.Build(); |
|
|
|
|
|
|
|
|
if (builder.Configuration.GetValue<string>("IsClusterEnv") == bool.TrueString) |
|
|
|
|
|
{ |
|
|
|
|
|
builder.Services.AddDataProtection(opts => |
|
|
|
|
|
{ |
|
|
|
|
|
opts.ApplicationDiscriminator = "eshop.webmvc"; |
|
|
|
|
|
}) |
|
|
|
|
|
.PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect(builder.Configuration["DPConnectionString"]), "DataProtection-Keys"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Adds all Http client services
|
|
|
|
|
|
static void AddHttpClientServices(WebApplicationBuilder builder) |
|
|
|
|
|
{ |
|
|
|
|
|
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); |
|
|
|
|
|
|
|
|
|
|
|
//register delegating handlers
|
|
|
|
|
|
builder.Services.AddTransient<HttpClientAuthorizationDelegatingHandler>() |
|
|
|
|
|
.AddTransient<HttpClientRequestIdDelegatingHandler>(); |
|
|
|
|
|
|
|
|
|
|
|
//set 5 min as the lifetime for each HttpMessageHandler int the pool
|
|
|
|
|
|
builder.Services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(TimeSpan.FromMinutes(5)); |
|
|
|
|
|
|
|
|
|
|
|
//add http client services
|
|
|
|
|
|
builder.Services.AddHttpClient<IBasketService, BasketService>() |
|
|
|
|
|
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes
|
|
|
|
|
|
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>(); |
|
|
|
|
|
|
|
|
|
|
|
builder.Services.AddHttpClient<ICatalogService, CatalogService>(); |
|
|
|
|
|
|
|
|
|
|
|
builder.Services.AddHttpClient<IOrderingService, OrderingService>() |
|
|
|
|
|
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() |
|
|
|
|
|
.AddHttpMessageHandler<HttpClientRequestIdDelegatingHandler>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//add custom application services
|
|
|
|
|
|
builder.Services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void AddCustomAuthentication(WebApplicationBuilder builder) |
|
|
|
|
|
{ |
|
|
|
|
|
var identityUrl = builder.Configuration.GetValue<string>("IdentityUrl"); |
|
|
|
|
|
var callBackUrl = builder.Configuration.GetValue<string>("CallBackUrl"); |
|
|
|
|
|
var sessionCookieLifetime = builder.Configuration.GetValue("SessionCookieLifetimeMinutes", 60); |
|
|
|
|
|
|
|
|
|
|
|
// Add Authentication services
|
|
|
|
|
|
|
|
|
|
|
|
builder.Services.AddAuthentication(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; |
|
|
|
|
|
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; |
|
|
|
|
|
}) |
|
|
|
|
|
.AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime)) |
|
|
|
|
|
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; |
|
|
|
|
|
options.Authority = identityUrl.ToString(); |
|
|
|
|
|
options.SignedOutRedirectUri = callBackUrl.ToString(); |
|
|
|
|
|
options.ClientId = "mvc"; |
|
|
|
|
|
options.ClientSecret = "secret"; |
|
|
|
|
|
options.ResponseType = "code"; |
|
|
|
|
|
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("webshoppingagg"); |
|
|
|
|
|
options.Scope.Add("orders.signalrhub"); |
|
|
|
|
|
options.Scope.Add("webhooks"); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public partial class Program |
|
|
public partial class Program |
|
|
{ |
|
|
{ |
|
|
private static readonly string _namespace = typeof(Startup).Namespace; |
|
|
|
|
|
public static readonly string AppName = _namespace.Substring(_namespace.LastIndexOf('.', _namespace.LastIndexOf('.') - 1) + 1); |
|
|
|
|
|
|
|
|
public static readonly string AppName = typeof(Program).Assembly.GetName().Name; |
|
|
} |
|
|
} |