net6conversion (#642)

* 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 startup
This commit is contained in:
Steve Smith
2021-12-06 15:14:43 -05:00
committed by GitHub
parent 20b060aeb3
commit ce63e38a23
34 changed files with 499 additions and 728 deletions

View File

@@ -9,9 +9,9 @@ using Swashbuckle.AspNetCore.Annotations;
namespace Microsoft.eShopWeb.PublicApi.AuthEndpoints;
public class Authenticate : BaseAsyncEndpoint
public class Authenticate : EndpointBaseAsync
.WithRequest<AuthenticateRequest>
.WithResponse<AuthenticateResponse>
.WithActionResult<AuthenticateResponse>
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ITokenClaimsService _tokenClaimsService;

View File

@@ -10,9 +10,9 @@ using Swashbuckle.AspNetCore.Annotations;
namespace Microsoft.eShopWeb.PublicApi.CatalogBrandEndpoints;
public class List : BaseAsyncEndpoint
public class List : EndpointBaseAsync
.WithoutRequest
.WithResponse<ListCatalogBrandsResponse>
.WithActionResult<ListCatalogBrandsResponse>
{
private readonly IRepository<CatalogBrand> _catalogBrandRepository;
private readonly IMapper _mapper;

View File

@@ -13,9 +13,9 @@ using Swashbuckle.AspNetCore.Annotations;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Create : BaseAsyncEndpoint
public class Create : EndpointBaseAsync
.WithRequest<CreateCatalogItemRequest>
.WithResponse<CreateCatalogItemResponse>
.WithActionResult<CreateCatalogItemResponse>
{
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;

View File

@@ -11,9 +11,9 @@ using Swashbuckle.AspNetCore.Annotations;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Delete : BaseAsyncEndpoint
public class Delete : EndpointBaseAsync
.WithRequest<DeleteCatalogItemRequest>
.WithResponse<DeleteCatalogItemResponse>
.WithActionResult<DeleteCatalogItemResponse>
{
private readonly IRepository<CatalogItem> _itemRepository;

View File

@@ -8,9 +8,9 @@ using Swashbuckle.AspNetCore.Annotations;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class GetById : BaseAsyncEndpoint
public class GetById : EndpointBaseAsync
.WithRequest<GetByIdCatalogItemRequest>
.WithResponse<GetByIdCatalogItemResponse>
.WithActionResult<GetByIdCatalogItemResponse>
{
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;

View File

@@ -12,9 +12,9 @@ using Swashbuckle.AspNetCore.Annotations;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
public class ListPaged : BaseAsyncEndpoint
public class ListPaged : EndpointBaseAsync
.WithRequest<ListPagedCatalogItemRequest>
.WithResponse<ListPagedCatalogItemResponse>
.WithActionResult<ListPagedCatalogItemResponse>
{
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;

View File

@@ -12,9 +12,9 @@ using Swashbuckle.AspNetCore.Annotations;
namespace Microsoft.eShopWeb.PublicApi.CatalogItemEndpoints;
[Authorize(Roles = BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS, AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class Update : BaseAsyncEndpoint
public class Update : EndpointBaseAsync
.WithRequest<UpdateCatalogItemRequest>
.WithResponse<UpdateCatalogItemResponse>
.WithActionResult<UpdateCatalogItemResponse>
{
private readonly IRepository<CatalogItem> _itemRepository;
private readonly IUriComposer _uriComposer;

View File

@@ -10,9 +10,9 @@ using Swashbuckle.AspNetCore.Annotations;
namespace Microsoft.eShopWeb.PublicApi.CatalogTypeEndpoints;
public class List : BaseAsyncEndpoint
public class List : EndpointBaseAsync
.WithoutRequest
.WithResponse<ListCatalogTypesResponse>
.WithActionResult<ListCatalogTypesResponse>
{
private readonly IRepository<CatalogType> _catalogTypeRepository;
private readonly IMapper _mapper;

View File

@@ -5,7 +5,7 @@ using BlazorShared.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopWeb.ApplicationCore.Exceptions;
namespace Microsoft.eShopWeb.PublicApi.MiddleWares;
namespace Microsoft.eShopWeb.PublicApi.Middleware;
public class ExceptionMiddleware
{

View File

@@ -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 =>
{
public static async Task Main(string[] args)
config.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(config =>
{
config.RequireHttpsMetadata = false;
config.SaveToken = true;
config.TokenValidationParameters = new TokenValidationParameters
{
var host = CreateHostBuilder(args)
.Build();
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
try
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"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
var catalogContext = services.GetRequiredService<CatalogContext>();
await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory);
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
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.");
}
}
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
},
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();

View File

@@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ardalis.ApiEndpoints" Version="3.1.0" />
<PackageReference Include="Ardalis.ApiEndpoints" Version="4.0.0-pre1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
<PackageReference Include="MediatR" Version="9.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />

View File

@@ -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();
});
}
}