Browse Source
* Converting to net6 startup and integration test formats * Update to minimal api and using pagetitle * Adjust functional test fixtures Update ApiEndpoints package version * Finish migration to minimal api startupmain
committed by
GitHub
34 changed files with 485 additions and 714 deletions
@ -1,54 +1,49 @@ |
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using BlazorAdmin; |
|||
using BlazorAdmin.Services; |
|||
using Blazored.LocalStorage; |
|||
using BlazorShared; |
|||
using BlazorShared.Models; |
|||
using Microsoft.AspNetCore.Components.Authorization; |
|||
using Microsoft.AspNetCore.Components.Web; |
|||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace BlazorAdmin; |
|||
var builder = WebAssemblyHostBuilder.CreateDefault(args); |
|||
builder.RootComponents.Add<App>("#admin"); |
|||
builder.RootComponents.Add<HeadOutlet>("head::after"); |
|||
|
|||
public class Program |
|||
{ |
|||
public static async Task Main(string[] args) |
|||
{ |
|||
var builder = WebAssemblyHostBuilder.CreateDefault(args); |
|||
builder.RootComponents.Add<App>("#admin"); |
|||
|
|||
var configSection = builder.Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME); |
|||
builder.Services.Configure<BaseUrlConfiguration>(configSection); |
|||
var configSection = builder.Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME); |
|||
builder.Services.Configure<BaseUrlConfiguration>(configSection); |
|||
|
|||
builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); |
|||
builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); |
|||
|
|||
builder.Services.AddScoped<ToastService>(); |
|||
builder.Services.AddScoped<HttpService>(); |
|||
builder.Services.AddScoped<ToastService>(); |
|||
builder.Services.AddScoped<HttpService>(); |
|||
|
|||
builder.Services.AddBlazoredLocalStorage(); |
|||
builder.Services.AddBlazoredLocalStorage(); |
|||
|
|||
builder.Services.AddAuthorizationCore(); |
|||
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>(); |
|||
builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>()); |
|||
builder.Services.AddAuthorizationCore(); |
|||
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>(); |
|||
builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>()); |
|||
|
|||
builder.Services.AddBlazorServices(); |
|||
builder.Services.AddBlazorServices(); |
|||
|
|||
builder.Logging.AddConfiguration(builder.Configuration.GetRequiredSection("Logging")); |
|||
builder.Logging.AddConfiguration(builder.Configuration.GetRequiredSection("Logging")); |
|||
|
|||
await ClearLocalStorageCache(builder.Services); |
|||
await ClearLocalStorageCache(builder.Services); |
|||
|
|||
await builder.Build().RunAsync(); |
|||
} |
|||
await builder.Build().RunAsync(); |
|||
|
|||
private static async Task ClearLocalStorageCache(IServiceCollection services) |
|||
{ |
|||
var sp = services.BuildServiceProvider(); |
|||
var localStorageService = sp.GetRequiredService<ILocalStorageService>(); |
|||
static async Task ClearLocalStorageCache(IServiceCollection services) |
|||
{ |
|||
var sp = services.BuildServiceProvider(); |
|||
var localStorageService = sp.GetRequiredService<ILocalStorageService>(); |
|||
|
|||
await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name); |
|||
await localStorageService.RemoveItemAsync(typeof(CatalogType).Name); |
|||
} |
|||
await localStorageService.RemoveItemAsync(typeof(CatalogBrand).Name); |
|||
await localStorageService.RemoveItemAsync(typeof(CatalogType).Name); |
|||
} |
|||
|
|||
@ -1,50 +1,186 @@ |
|||
using System; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using BlazorShared; |
|||
using BlazorShared.Models; |
|||
using MediatR; |
|||
using Microsoft.AspNetCore.Authentication.JwtBearer; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.eShopWeb; |
|||
using Microsoft.eShopWeb.ApplicationCore.Constants; |
|||
using Microsoft.eShopWeb.ApplicationCore.Interfaces; |
|||
using Microsoft.eShopWeb.ApplicationCore.Services; |
|||
using Microsoft.eShopWeb.Infrastructure.Data; |
|||
using Microsoft.eShopWeb.Infrastructure.Identity; |
|||
using Microsoft.eShopWeb.Infrastructure.Logging; |
|||
using Microsoft.eShopWeb.PublicApi; |
|||
using Microsoft.eShopWeb.PublicApi.Middleware; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.IdentityModel.Tokens; |
|||
using Microsoft.OpenApi.Models; |
|||
|
|||
namespace Microsoft.eShopWeb.PublicApi; |
|||
var builder = WebApplication.CreateBuilder(args); |
|||
|
|||
public class Program |
|||
builder.Logging.AddConsole(); |
|||
|
|||
// use real database
|
|||
// Requires LocalDB which can be installed with SQL Server Express 2016
|
|||
// https://www.microsoft.com/en-us/download/details.aspx?id=54284
|
|||
builder.Services.AddDbContext<CatalogContext>(c => |
|||
c.UseSqlServer(builder.Configuration.GetConnectionString("CatalogConnection"))); |
|||
|
|||
// Add Identity DbContext
|
|||
builder.Services.AddDbContext<AppIdentityDbContext>(options => |
|||
options.UseSqlServer(builder.Configuration.GetConnectionString("IdentityConnection"))); |
|||
|
|||
builder.Services.AddIdentity<ApplicationUser, IdentityRole>() |
|||
.AddEntityFrameworkStores<AppIdentityDbContext>() |
|||
.AddDefaultTokenProviders(); |
|||
|
|||
builder.Services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>)); |
|||
builder.Services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>)); |
|||
builder.Services.Configure<CatalogSettings>(builder.Configuration); |
|||
builder.Services.AddSingleton<IUriComposer>(new UriComposer(builder.Configuration.Get<CatalogSettings>())); |
|||
builder.Services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>)); |
|||
builder.Services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>(); |
|||
|
|||
var configSection = builder.Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME); |
|||
builder.Services.Configure<BaseUrlConfiguration>(configSection); |
|||
var baseUrlConfig = configSection.Get<BaseUrlConfiguration>(); |
|||
|
|||
builder.Services.AddMemoryCache(); |
|||
|
|||
var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY); |
|||
builder.Services.AddAuthentication(config => |
|||
{ |
|||
config.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; |
|||
}) |
|||
.AddJwtBearer(config => |
|||
{ |
|||
public static async Task Main(string[] args) |
|||
config.RequireHttpsMetadata = false; |
|||
config.SaveToken = true; |
|||
config.TokenValidationParameters = new TokenValidationParameters |
|||
{ |
|||
var host = CreateHostBuilder(args) |
|||
.Build(); |
|||
|
|||
using (var scope = host.Services.CreateScope()) |
|||
{ |
|||
var services = scope.ServiceProvider; |
|||
var loggerFactory = services.GetRequiredService<ILoggerFactory>(); |
|||
try |
|||
{ |
|||
var catalogContext = services.GetRequiredService<CatalogContext>(); |
|||
await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory); |
|||
|
|||
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>(); |
|||
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>(); |
|||
await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
var logger = loggerFactory.CreateLogger<Program>(); |
|||
logger.LogError(ex, "An error occurred seeding the DB."); |
|||
} |
|||
} |
|||
ValidateIssuerSigningKey = true, |
|||
IssuerSigningKey = new SymmetricSecurityKey(key), |
|||
ValidateIssuer = false, |
|||
ValidateAudience = false |
|||
}; |
|||
}); |
|||
|
|||
host.Run(); |
|||
} |
|||
const string CORS_POLICY = "CorsPolicy"; |
|||
builder.Services.AddCors(options => |
|||
{ |
|||
options.AddPolicy(name: CORS_POLICY, |
|||
builder => |
|||
{ |
|||
builder.WithOrigins(baseUrlConfig.WebBase.Replace("host.docker.internal", "localhost").TrimEnd('/')); |
|||
builder.AllowAnyMethod(); |
|||
builder.AllowAnyHeader(); |
|||
}); |
|||
}); |
|||
|
|||
builder.Services.AddControllers(); |
|||
|
|||
builder.Services.AddMediatR(typeof(CatalogItem).Assembly); |
|||
builder.Services.AddAutoMapper(typeof(MappingProfile).Assembly); |
|||
|
|||
builder.Services.AddSwaggerGen(c => |
|||
{ |
|||
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); |
|||
c.EnableAnnotations(); |
|||
c.SchemaFilter<CustomSchemaFilters>(); |
|||
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme |
|||
{ |
|||
Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n
|
|||
Enter 'Bearer' [space] and then your token in the text input below. |
|||
\r\n\r\nExample: 'Bearer 12345abcdef'",
|
|||
Name = "Authorization", |
|||
In = ParameterLocation.Header, |
|||
Type = SecuritySchemeType.ApiKey, |
|||
Scheme = "Bearer" |
|||
}); |
|||
|
|||
public static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Host.CreateDefaultBuilder(args) |
|||
.ConfigureWebHostDefaults(webBuilder => |
|||
c.AddSecurityRequirement(new OpenApiSecurityRequirement() |
|||
{ |
|||
webBuilder.UseStartup<Startup>(); |
|||
{ |
|||
new OpenApiSecurityScheme |
|||
{ |
|||
Reference = new OpenApiReference |
|||
{ |
|||
Type = ReferenceType.SecurityScheme, |
|||
Id = "Bearer" |
|||
}, |
|||
Scheme = "oauth2", |
|||
Name = "Bearer", |
|||
In = ParameterLocation.Header, |
|||
|
|||
}, |
|||
new List<string>() |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
var app = builder.Build(); |
|||
|
|||
app.Logger.LogInformation("PublicApi App created..."); |
|||
|
|||
if (app.Environment.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
|
|||
app.UseMiddleware<ExceptionMiddleware>(); |
|||
|
|||
app.UseHttpsRedirection(); |
|||
|
|||
app.UseRouting(); |
|||
|
|||
app.UseCors(CORS_POLICY); |
|||
|
|||
app.UseAuthorization(); |
|||
|
|||
// Enable middleware to serve generated Swagger as a JSON endpoint.
|
|||
app.UseSwagger(); |
|||
|
|||
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
|
|||
// specifying the Swagger JSON endpoint.
|
|||
app.UseSwaggerUI(c => |
|||
{ |
|||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); |
|||
}); |
|||
|
|||
app.UseEndpoints(endpoints => |
|||
{ |
|||
endpoints.MapControllers(); |
|||
}); |
|||
|
|||
app.Logger.LogInformation("Seeding Database..."); |
|||
|
|||
using (var scope = app.Services.CreateScope()) |
|||
{ |
|||
var scopedProvider = scope.ServiceProvider; |
|||
try |
|||
{ |
|||
var catalogContext = scopedProvider.GetRequiredService<CatalogContext>(); |
|||
await CatalogContextSeed.SeedAsync(catalogContext, app.Logger); |
|||
|
|||
var userManager = scopedProvider.GetRequiredService<UserManager<ApplicationUser>>(); |
|||
var roleManager = scopedProvider.GetRequiredService<RoleManager<IdentityRole>>(); |
|||
await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
app.Logger.LogError(ex, "An error occurred seeding the DB."); |
|||
} |
|||
} |
|||
|
|||
app.Logger.LogInformation("LAUNCHING PublicApi"); |
|||
app.Run(); |
|||
|
|||
@ -1,208 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using AutoMapper; |
|||
using BlazorShared; |
|||
using MediatR; |
|||
using Microsoft.AspNetCore.Authentication.JwtBearer; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.eShopWeb.ApplicationCore.Constants; |
|||
using Microsoft.eShopWeb.ApplicationCore.Entities; |
|||
using Microsoft.eShopWeb.ApplicationCore.Interfaces; |
|||
using Microsoft.eShopWeb.ApplicationCore.Services; |
|||
using Microsoft.eShopWeb.Infrastructure.Data; |
|||
using Microsoft.eShopWeb.Infrastructure.Identity; |
|||
using Microsoft.eShopWeb.Infrastructure.Logging; |
|||
using Microsoft.eShopWeb.Infrastructure.Services; |
|||
using Microsoft.eShopWeb.PublicApi.MiddleWares; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.IdentityModel.Tokens; |
|||
using Microsoft.OpenApi.Models; |
|||
|
|||
namespace Microsoft.eShopWeb.PublicApi; |
|||
|
|||
public class Startup |
|||
{ |
|||
private const string CORS_POLICY = "CorsPolicy"; |
|||
|
|||
public Startup(IConfiguration configuration) |
|||
{ |
|||
Configuration = configuration; |
|||
} |
|||
|
|||
public IConfiguration Configuration { get; } |
|||
|
|||
public void ConfigureDevelopmentServices(IServiceCollection services) |
|||
{ |
|||
// use in-memory database
|
|||
ConfigureInMemoryDatabases(services); |
|||
|
|||
// use real database
|
|||
//ConfigureProductionServices(services);
|
|||
} |
|||
|
|||
public void ConfigureDockerServices(IServiceCollection services) |
|||
{ |
|||
ConfigureDevelopmentServices(services); |
|||
} |
|||
|
|||
private void ConfigureInMemoryDatabases(IServiceCollection services) |
|||
{ |
|||
services.AddDbContext<CatalogContext>(c => |
|||
c.UseInMemoryDatabase("Catalog")); |
|||
|
|||
services.AddDbContext<AppIdentityDbContext>(options => |
|||
options.UseInMemoryDatabase("Identity")); |
|||
|
|||
ConfigureServices(services); |
|||
} |
|||
|
|||
public void ConfigureProductionServices(IServiceCollection services) |
|||
{ |
|||
// use real database
|
|||
// Requires LocalDB which can be installed with SQL Server Express 2016
|
|||
// https://www.microsoft.com/en-us/download/details.aspx?id=54284
|
|||
services.AddDbContext<CatalogContext>(c => |
|||
c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection"))); |
|||
|
|||
// Add Identity DbContext
|
|||
services.AddDbContext<AppIdentityDbContext>(options => |
|||
options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection"))); |
|||
|
|||
ConfigureServices(services); |
|||
} |
|||
|
|||
public void ConfigureTestingServices(IServiceCollection services) |
|||
{ |
|||
ConfigureInMemoryDatabases(services); |
|||
} |
|||
|
|||
|
|||
// This method gets called by the runtime. Use this method to add services to the container.
|
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddIdentity<ApplicationUser, IdentityRole>() |
|||
.AddEntityFrameworkStores<AppIdentityDbContext>() |
|||
.AddDefaultTokenProviders(); |
|||
|
|||
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>)); |
|||
services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>)); |
|||
services.Configure<CatalogSettings>(Configuration); |
|||
services.AddSingleton<IUriComposer>(new UriComposer(Configuration.Get<CatalogSettings>())); |
|||
services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>)); |
|||
services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>(); |
|||
|
|||
var configSection = Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME); |
|||
services.Configure<BaseUrlConfiguration>(configSection); |
|||
var baseUrlConfig = configSection.Get<BaseUrlConfiguration>(); |
|||
|
|||
services.AddMemoryCache(); |
|||
|
|||
var key = Encoding.ASCII.GetBytes(AuthorizationConstants.JWT_SECRET_KEY); |
|||
services.AddAuthentication(config => |
|||
{ |
|||
config.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; |
|||
}) |
|||
.AddJwtBearer(config => |
|||
{ |
|||
config.RequireHttpsMetadata = false; |
|||
config.SaveToken = true; |
|||
config.TokenValidationParameters = new TokenValidationParameters |
|||
{ |
|||
ValidateIssuerSigningKey = true, |
|||
IssuerSigningKey = new SymmetricSecurityKey(key), |
|||
ValidateIssuer = false, |
|||
ValidateAudience = false |
|||
}; |
|||
}); |
|||
|
|||
services.AddCors(options => |
|||
{ |
|||
options.AddPolicy(name: CORS_POLICY, |
|||
builder => |
|||
{ |
|||
builder.WithOrigins(baseUrlConfig.WebBase.Replace("host.docker.internal", "localhost").TrimEnd('/')); |
|||
builder.AllowAnyMethod(); |
|||
builder.AllowAnyHeader(); |
|||
}); |
|||
}); |
|||
|
|||
services.AddControllers(); |
|||
services.AddMediatR(typeof(CatalogItem).Assembly); |
|||
|
|||
services.AddAutoMapper(typeof(Startup).Assembly); |
|||
services.AddSwaggerGen(c => |
|||
{ |
|||
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); |
|||
c.EnableAnnotations(); |
|||
c.SchemaFilter<CustomSchemaFilters>(); |
|||
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme |
|||
{ |
|||
Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n
|
|||
Enter 'Bearer' [space] and then your token in the text input below. |
|||
\r\n\r\nExample: 'Bearer 12345abcdef'",
|
|||
Name = "Authorization", |
|||
In = ParameterLocation.Header, |
|||
Type = SecuritySchemeType.ApiKey, |
|||
Scheme = "Bearer" |
|||
}); |
|||
|
|||
c.AddSecurityRequirement(new OpenApiSecurityRequirement() |
|||
{ |
|||
{ |
|||
new OpenApiSecurityScheme |
|||
{ |
|||
Reference = new OpenApiReference |
|||
{ |
|||
Type = ReferenceType.SecurityScheme, |
|||
Id = "Bearer" |
|||
}, |
|||
Scheme = "oauth2", |
|||
Name = "Bearer", |
|||
In = ParameterLocation.Header, |
|||
|
|||
}, |
|||
new List<string>() |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
|||
{ |
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
|
|||
app.UseMiddleware<ExceptionMiddleware>(); |
|||
|
|||
app.UseHttpsRedirection(); |
|||
|
|||
app.UseRouting(); |
|||
|
|||
app.UseCors(CORS_POLICY); |
|||
|
|||
app.UseAuthorization(); |
|||
|
|||
// Enable middleware to serve generated Swagger as a JSON endpoint.
|
|||
app.UseSwagger(); |
|||
|
|||
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
|
|||
// specifying the Swagger JSON endpoint.
|
|||
app.UseSwaggerUI(c => |
|||
{ |
|||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); |
|||
}); |
|||
|
|||
app.UseEndpoints(endpoints => |
|||
{ |
|||
endpoints.MapControllers(); |
|||
}); |
|||
} |
|||
} |
|||
@ -1,49 +1,198 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using System.Net.Mime; |
|||
using Ardalis.ListStartupServices; |
|||
using BlazorAdmin; |
|||
using BlazorAdmin.Services; |
|||
using Blazored.LocalStorage; |
|||
using BlazorShared; |
|||
using Microsoft.AspNetCore.Authentication.Cookies; |
|||
using Microsoft.AspNetCore.Diagnostics.HealthChecks; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Microsoft.AspNetCore.Mvc.ApplicationModels; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.eShopWeb; |
|||
using Microsoft.eShopWeb.ApplicationCore.Interfaces; |
|||
using Microsoft.eShopWeb.Infrastructure.Data; |
|||
using Microsoft.eShopWeb.Infrastructure.Identity; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.eShopWeb.Web; |
|||
using Microsoft.eShopWeb.Web.Configuration; |
|||
using Microsoft.eShopWeb.Web.HealthChecks; |
|||
using Microsoft.Extensions.Diagnostics.HealthChecks; |
|||
|
|||
namespace Microsoft.eShopWeb.Web; |
|||
var builder = WebApplication.CreateBuilder(args); |
|||
|
|||
public class Program |
|||
builder.Logging.AddConsole(); |
|||
|
|||
// use real database
|
|||
// Requires LocalDB which can be installed with SQL Server Express 2016
|
|||
// https://www.microsoft.com/en-us/download/details.aspx?id=54284
|
|||
builder.Services.AddDbContext<CatalogContext>(c => |
|||
c.UseSqlServer(builder.Configuration.GetConnectionString("CatalogConnection"))); |
|||
|
|||
// Add Identity DbContext
|
|||
builder.Services.AddDbContext<AppIdentityDbContext>(options => |
|||
options.UseSqlServer(builder.Configuration.GetConnectionString("IdentityConnection"))); |
|||
|
|||
builder.Services.AddCookieSettings(); |
|||
|
|||
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) |
|||
.AddCookie(options => |
|||
{ |
|||
options.Cookie.HttpOnly = true; |
|||
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; |
|||
options.Cookie.SameSite = SameSiteMode.Lax; |
|||
}); |
|||
|
|||
builder.Services.AddIdentity<ApplicationUser, IdentityRole>() |
|||
.AddDefaultUI() |
|||
.AddEntityFrameworkStores<AppIdentityDbContext>() |
|||
.AddDefaultTokenProviders(); |
|||
|
|||
builder.Services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>(); |
|||
|
|||
builder.Services.AddCoreServices(builder.Configuration); |
|||
builder.Services.AddWebServices(builder.Configuration); |
|||
|
|||
// Add memory cache services
|
|||
builder.Services.AddMemoryCache(); |
|||
builder.Services.AddRouting(options => |
|||
{ |
|||
// Replace the type and the name used to refer to it with your own
|
|||
// IOutboundParameterTransformer implementation
|
|||
options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer); |
|||
}); |
|||
|
|||
builder.Services.AddMvc(options => |
|||
{ |
|||
options.Conventions.Add(new RouteTokenTransformerConvention( |
|||
new SlugifyParameterTransformer())); |
|||
|
|||
}); |
|||
builder.Services.AddControllersWithViews(); |
|||
builder.Services.AddRazorPages(options => |
|||
{ |
|||
options.Conventions.AuthorizePage("/Basket/Checkout"); |
|||
}); |
|||
builder.Services.AddHttpContextAccessor(); |
|||
builder.Services |
|||
.AddHealthChecks() |
|||
.AddCheck<ApiHealthCheck>("api_health_check", tags: new[] { "apiHealthCheck" }) |
|||
.AddCheck<HomePageHealthCheck>("home_page_health_check", tags: new[] { "homePageHealthCheck" }); |
|||
builder.Services.Configure<ServiceConfig>(config => |
|||
{ |
|||
config.Services = new List<ServiceDescriptor>(builder.Services); |
|||
config.Path = "/allservices"; |
|||
}); |
|||
|
|||
// blazor configuration
|
|||
var configSection = builder.Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME); |
|||
builder.Services.Configure<BaseUrlConfiguration>(configSection); |
|||
var baseUrlConfig = configSection.Get<BaseUrlConfiguration>(); |
|||
|
|||
// Blazor Admin Required Services for Prerendering
|
|||
builder.Services.AddScoped<HttpClient>(s => new HttpClient |
|||
{ |
|||
BaseAddress = new Uri(baseUrlConfig.WebBase) |
|||
}); |
|||
|
|||
// add blazor services
|
|||
builder.Services.AddBlazoredLocalStorage(); |
|||
builder.Services.AddServerSideBlazor(); |
|||
|
|||
|
|||
builder.Services.AddScoped<ToastService>(); |
|||
builder.Services.AddScoped<HttpService>(); |
|||
builder.Services.AddBlazorServices(); |
|||
|
|||
builder.Services.AddDatabaseDeveloperPageExceptionFilter(); |
|||
|
|||
//_services = services; // used to debug registered services
|
|||
|
|||
var app = builder.Build(); |
|||
|
|||
app.Logger.LogInformation("App created..."); |
|||
|
|||
var catalogBaseUrl = builder.Configuration.GetValue(typeof(string), "CatalogBaseUrl") as string; |
|||
if (!string.IsNullOrEmpty(catalogBaseUrl)) |
|||
{ |
|||
public static async Task Main(string[] args) |
|||
app.Use((context, next) => |
|||
{ |
|||
var host = CreateHostBuilder(args) |
|||
.Build(); |
|||
context.Request.PathBase = new PathString(catalogBaseUrl); |
|||
return next(); |
|||
}); |
|||
} |
|||
|
|||
using (var scope = host.Services.CreateScope()) |
|||
app.UseHealthChecks("/health", |
|||
new HealthCheckOptions |
|||
{ |
|||
ResponseWriter = async (context, report) => |
|||
{ |
|||
var services = scope.ServiceProvider; |
|||
var loggerFactory = services.GetRequiredService<ILoggerFactory>(); |
|||
try |
|||
{ |
|||
var catalogContext = services.GetRequiredService<CatalogContext>(); |
|||
await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory); |
|||
|
|||
var userManager = services.GetRequiredService<UserManager<ApplicationUser>>(); |
|||
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>(); |
|||
await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager); |
|||
} |
|||
catch (Exception ex) |
|||
var result = new |
|||
{ |
|||
var logger = loggerFactory.CreateLogger<Program>(); |
|||
logger.LogError(ex, "An error occurred seeding the DB."); |
|||
} |
|||
status = report.Status.ToString(), |
|||
errors = report.Entries.Select(e => new |
|||
{ |
|||
key = e.Key, |
|||
value = Enum.GetName(typeof(HealthStatus), e.Value.Status) |
|||
}) |
|||
}.ToJson(); |
|||
context.Response.ContentType = MediaTypeNames.Application.Json; |
|||
await context.Response.WriteAsync(result); |
|||
} |
|||
}); |
|||
if (app.Environment.IsDevelopment()) |
|||
{ |
|||
app.Logger.LogInformation("Adding Development middleware..."); |
|||
app.UseDeveloperExceptionPage(); |
|||
app.UseShowAllServicesMiddleware(); |
|||
app.UseMigrationsEndPoint(); |
|||
app.UseWebAssemblyDebugging(); |
|||
} |
|||
else |
|||
{ |
|||
app.Logger.LogInformation("Adding non-Development middleware..."); |
|||
app.UseExceptionHandler("/Error"); |
|||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|||
app.UseHsts(); |
|||
} |
|||
|
|||
host.Run(); |
|||
} |
|||
app.UseHttpsRedirection(); |
|||
app.UseBlazorFrameworkFiles(); |
|||
app.UseStaticFiles(); |
|||
app.UseRouting(); |
|||
|
|||
public static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Host.CreateDefaultBuilder(args) |
|||
.ConfigureWebHostDefaults(webBuilder => |
|||
{ |
|||
webBuilder.UseStartup<Startup>(); |
|||
}); |
|||
app.UseCookiePolicy(); |
|||
app.UseAuthentication(); |
|||
app.UseAuthorization(); |
|||
|
|||
app.UseEndpoints(endpoints => |
|||
{ |
|||
endpoints.MapControllerRoute("default", "{controller:slugify=Home}/{action:slugify=Index}/{id?}"); |
|||
endpoints.MapRazorPages(); |
|||
endpoints.MapHealthChecks("home_page_health_check", new HealthCheckOptions { Predicate = check => check.Tags.Contains("homePageHealthCheck") }); |
|||
endpoints.MapHealthChecks("api_health_check", new HealthCheckOptions { Predicate = check => check.Tags.Contains("apiHealthCheck") }); |
|||
//endpoints.MapBlazorHub("/admin");
|
|||
endpoints.MapFallbackToFile("index.html"); |
|||
}); |
|||
|
|||
app.Logger.LogInformation("Seeding Database..."); |
|||
|
|||
using (var scope = app.Services.CreateScope()) |
|||
{ |
|||
var scopedProvider = scope.ServiceProvider; |
|||
try |
|||
{ |
|||
var catalogContext = scopedProvider.GetRequiredService<CatalogContext>(); |
|||
await CatalogContextSeed.SeedAsync(catalogContext, app.Logger); |
|||
|
|||
var userManager = scopedProvider.GetRequiredService<UserManager<ApplicationUser>>(); |
|||
var roleManager = scopedProvider.GetRequiredService<RoleManager<IdentityRole>>(); |
|||
await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
app.Logger.LogError(ex, "An error occurred seeding the DB."); |
|||
} |
|||
} |
|||
|
|||
app.Logger.LogInformation("LAUNCHING"); |
|||
app.Run(); |
|||
|
|||
@ -1,241 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Net.Http; |
|||
using System.Net.Mime; |
|||
using Ardalis.ListStartupServices; |
|||
using BlazorAdmin; |
|||
using BlazorAdmin.Services; |
|||
using Blazored.LocalStorage; |
|||
using BlazorShared; |
|||
using Microsoft.AspNetCore.Authentication.Cookies; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.DataProtection; |
|||
using Microsoft.AspNetCore.Diagnostics.HealthChecks; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Microsoft.AspNetCore.Mvc.ApplicationModels; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.eShopWeb.ApplicationCore.Interfaces; |
|||
using Microsoft.eShopWeb.Infrastructure.Data; |
|||
using Microsoft.eShopWeb.Infrastructure.Identity; |
|||
using Microsoft.eShopWeb.Web.Configuration; |
|||
using Microsoft.eShopWeb.Web.HealthChecks; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Diagnostics.HealthChecks; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace Microsoft.eShopWeb.Web; |
|||
|
|||
public class Startup |
|||
{ |
|||
private IServiceCollection _services; |
|||
|
|||
public Startup(IConfiguration configuration) |
|||
{ |
|||
Configuration = configuration; |
|||
} |
|||
|
|||
public IConfiguration Configuration { get; } |
|||
|
|||
public void ConfigureDevelopmentServices(IServiceCollection services) |
|||
{ |
|||
// use in-memory database
|
|||
ConfigureInMemoryDatabases(services); |
|||
|
|||
// use real database
|
|||
//ConfigureProductionServices(services);
|
|||
} |
|||
|
|||
public void ConfigureDockerServices(IServiceCollection services) |
|||
{ |
|||
services.AddDataProtection() |
|||
.SetApplicationName("eshopwebmvc") |
|||
.PersistKeysToFileSystem(new DirectoryInfo(@"./")); |
|||
|
|||
ConfigureDevelopmentServices(services); |
|||
} |
|||
|
|||
private void ConfigureInMemoryDatabases(IServiceCollection services) |
|||
{ |
|||
// use in-memory database
|
|||
services.AddDbContext<CatalogContext>(c => |
|||
c.UseInMemoryDatabase("Catalog")); |
|||
|
|||
// Add Identity DbContext
|
|||
services.AddDbContext<AppIdentityDbContext>(options => |
|||
options.UseInMemoryDatabase("Identity")); |
|||
|
|||
ConfigureServices(services); |
|||
} |
|||
|
|||
public void ConfigureProductionServices(IServiceCollection services) |
|||
{ |
|||
// use real database
|
|||
// Requires LocalDB which can be installed with SQL Server Express 2016
|
|||
// https://www.microsoft.com/en-us/download/details.aspx?id=54284
|
|||
services.AddDbContext<CatalogContext>(c => |
|||
c.UseSqlServer(Configuration.GetConnectionString("CatalogConnection"))); |
|||
|
|||
// Add Identity DbContext
|
|||
services.AddDbContext<AppIdentityDbContext>(options => |
|||
options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection"))); |
|||
|
|||
ConfigureServices(services); |
|||
} |
|||
|
|||
public void ConfigureTestingServices(IServiceCollection services) |
|||
{ |
|||
ConfigureInMemoryDatabases(services); |
|||
} |
|||
|
|||
|
|||
// This method gets called by the runtime. Use this method to add services to the container.
|
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddCookieSettings(); |
|||
|
|||
|
|||
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) |
|||
.AddCookie(options => |
|||
{ |
|||
options.Cookie.HttpOnly = true; |
|||
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; |
|||
options.Cookie.SameSite = SameSiteMode.Lax; |
|||
}); |
|||
|
|||
services.AddIdentity<ApplicationUser, IdentityRole>() |
|||
.AddDefaultUI() |
|||
.AddEntityFrameworkStores<AppIdentityDbContext>() |
|||
.AddDefaultTokenProviders(); |
|||
|
|||
services.AddScoped<ITokenClaimsService, IdentityTokenClaimService>(); |
|||
|
|||
services.AddCoreServices(Configuration); |
|||
services.AddWebServices(Configuration); |
|||
|
|||
// Add memory cache services
|
|||
services.AddMemoryCache(); |
|||
services.AddRouting(options => |
|||
{ |
|||
// Replace the type and the name used to refer to it with your own
|
|||
// IOutboundParameterTransformer implementation
|
|||
options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer); |
|||
}); |
|||
services.AddMvc(options => |
|||
{ |
|||
options.Conventions.Add(new RouteTokenTransformerConvention( |
|||
new SlugifyParameterTransformer())); |
|||
|
|||
}); |
|||
services.AddControllersWithViews(); |
|||
services.AddRazorPages(options => |
|||
{ |
|||
options.Conventions.AuthorizePage("/Basket/Checkout"); |
|||
}); |
|||
services.AddHttpContextAccessor(); |
|||
services |
|||
.AddHealthChecks() |
|||
.AddCheck<ApiHealthCheck>("api_health_check", tags: new[] { "apiHealthCheck" }) |
|||
.AddCheck<HomePageHealthCheck>("home_page_health_check", tags: new[] { "homePageHealthCheck" }); |
|||
services.Configure<ServiceConfig>(config => |
|||
{ |
|||
config.Services = new List<ServiceDescriptor>(services); |
|||
|
|||
config.Path = "/allservices"; |
|||
}); |
|||
|
|||
var configSection = Configuration.GetRequiredSection(BaseUrlConfiguration.CONFIG_NAME); |
|||
services.Configure<BaseUrlConfiguration>(configSection); |
|||
var baseUrlConfig = configSection.Get<BaseUrlConfiguration>(); |
|||
|
|||
// Blazor Admin Required Services for Prerendering
|
|||
services.AddScoped<HttpClient>(s => new HttpClient |
|||
{ |
|||
BaseAddress = new Uri(baseUrlConfig.WebBase) |
|||
}); |
|||
|
|||
// add blazor services
|
|||
services.AddBlazoredLocalStorage(); |
|||
services.AddServerSideBlazor(); |
|||
|
|||
|
|||
services.AddScoped<ToastService>(); |
|||
services.AddScoped<HttpService>(); |
|||
services.AddBlazorServices(); |
|||
|
|||
services.AddDatabaseDeveloperPageExceptionFilter(); |
|||
|
|||
_services = services; // used to debug registered services
|
|||
} |
|||
|
|||
|
|||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
|||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
|||
{ |
|||
var catalogBaseUrl = Configuration.GetValue(typeof(string), "CatalogBaseUrl") as string; |
|||
if (!string.IsNullOrEmpty(catalogBaseUrl)) |
|||
{ |
|||
app.Use((context, next) => |
|||
{ |
|||
context.Request.PathBase = new PathString(catalogBaseUrl); |
|||
return next(); |
|||
}); |
|||
} |
|||
|
|||
app.UseHealthChecks("/health", |
|||
new HealthCheckOptions |
|||
{ |
|||
ResponseWriter = async (context, report) => |
|||
{ |
|||
var result = new |
|||
{ |
|||
status = report.Status.ToString(), |
|||
errors = report.Entries.Select(e => new |
|||
{ |
|||
key = e.Key, |
|||
value = Enum.GetName(typeof(HealthStatus), e.Value.Status) |
|||
}) |
|||
}.ToJson(); |
|||
context.Response.ContentType = MediaTypeNames.Application.Json; |
|||
await context.Response.WriteAsync(result); |
|||
} |
|||
}); |
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
app.UseShowAllServicesMiddleware(); |
|||
app.UseMigrationsEndPoint(); |
|||
app.UseWebAssemblyDebugging(); |
|||
} |
|||
else |
|||
{ |
|||
app.UseExceptionHandler("/Error"); |
|||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|||
app.UseHsts(); |
|||
} |
|||
|
|||
app.UseHttpsRedirection(); |
|||
app.UseBlazorFrameworkFiles(); |
|||
app.UseStaticFiles(); |
|||
app.UseRouting(); |
|||
|
|||
app.UseCookiePolicy(); |
|||
app.UseAuthentication(); |
|||
app.UseAuthorization(); |
|||
|
|||
app.UseEndpoints(endpoints => |
|||
{ |
|||
endpoints.MapControllerRoute("default", "{controller:slugify=Home}/{action:slugify=Index}/{id?}"); |
|||
endpoints.MapRazorPages(); |
|||
endpoints.MapHealthChecks("home_page_health_check", new HealthCheckOptions { Predicate = check => check.Tags.Contains("homePageHealthCheck") }); |
|||
endpoints.MapHealthChecks("api_health_check", new HealthCheckOptions { Predicate = check => check.Tags.Contains("apiHealthCheck") }); |
|||
//endpoints.MapBlazorHub("/admin");
|
|||
endpoints.MapFallbackToFile("index.html"); |
|||
}); |
|||
} |
|||
|
|||
} |
|||
@ -1,78 +1,43 @@ |
|||
using System; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore.Mvc.Testing; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.eShopWeb.Infrastructure.Data; |
|||
using Microsoft.eShopWeb.Infrastructure.Identity; |
|||
using Microsoft.eShopWeb.PublicApi; |
|||
using Microsoft.eShopWeb.PublicApi.AuthEndpoints; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Hosting; |
|||
|
|||
namespace Microsoft.eShopWeb.FunctionalTests.PublicApi; |
|||
|
|||
public class ApiTestFixture : WebApplicationFactory<Startup> |
|||
public class TestApiApplication : WebApplicationFactory<Authenticate> |
|||
{ |
|||
protected override void ConfigureWebHost(IWebHostBuilder builder) |
|||
private readonly string _environment = "Testing"; |
|||
|
|||
protected override IHost CreateHost(IHostBuilder builder) |
|||
{ |
|||
builder.UseEnvironment("Testing"); |
|||
builder.UseEnvironment(_environment); |
|||
|
|||
// Add mock/test services to the builder here
|
|||
builder.ConfigureServices(services => |
|||
{ |
|||
services.AddEntityFrameworkInMemoryDatabase(); |
|||
|
|||
// Create a new service provider.
|
|||
var provider = services |
|||
.AddEntityFrameworkInMemoryDatabase() |
|||
.BuildServiceProvider(); |
|||
|
|||
// Add a database context (ApplicationDbContext) using an in-memory
|
|||
// database for testing.
|
|||
services.AddDbContext<CatalogContext>(options => |
|||
services.AddScoped(sp => |
|||
{ |
|||
options.UseInMemoryDatabase("InMemoryDbForTesting"); |
|||
options.UseInternalServiceProvider(provider); |
|||
// Replace SQLite with in-memory database for tests
|
|||
return new DbContextOptionsBuilder<CatalogContext>() |
|||
.UseInMemoryDatabase("DbForPublicApi") |
|||
.UseApplicationServiceProvider(sp) |
|||
.Options; |
|||
}); |
|||
|
|||
services.AddDbContext<AppIdentityDbContext>(options => |
|||
services.AddScoped(sp => |
|||
{ |
|||
options.UseInMemoryDatabase("Identity"); |
|||
options.UseInternalServiceProvider(provider); |
|||
// Replace SQLite with in-memory database for tests
|
|||
return new DbContextOptionsBuilder<AppIdentityDbContext>() |
|||
.UseInMemoryDatabase("IdentityDbForPublicApi") |
|||
.UseApplicationServiceProvider(sp) |
|||
.Options; |
|||
}); |
|||
|
|||
// Build the service provider.
|
|||
var sp = services.BuildServiceProvider(); |
|||
|
|||
// Create a scope to obtain a reference to the database
|
|||
// context (ApplicationDbContext).
|
|||
using (var scope = sp.CreateScope()) |
|||
{ |
|||
var scopedServices = scope.ServiceProvider; |
|||
var db = scopedServices.GetRequiredService<CatalogContext>(); |
|||
var loggerFactory = scopedServices.GetRequiredService<ILoggerFactory>(); |
|||
|
|||
var logger = scopedServices |
|||
.GetRequiredService<ILogger<ApiTestFixture>>(); |
|||
|
|||
// Ensure the database is created.
|
|||
db.Database.EnsureCreated(); |
|||
|
|||
try |
|||
{ |
|||
// Seed the database with test data.
|
|||
CatalogContextSeed.SeedAsync(db, loggerFactory).Wait(); |
|||
|
|||
// seed sample user data
|
|||
var userManager = scopedServices.GetRequiredService<UserManager<ApplicationUser>>(); |
|||
var roleManager = scopedServices.GetRequiredService<RoleManager<IdentityRole>>(); |
|||
AppIdentityDbContextSeed.SeedAsync(userManager, roleManager).Wait(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
logger.LogError(ex, $"An error occurred seeding the " + |
|||
"database with test messages. Error: {ex.Message}"); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
return base.CreateHost(builder); |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue