Removed ASP.NET Core's (2.1) default model validation behavior

This commit is contained in:
rafsanulhasan 2018-08-13 21:20:55 +06:00
parent 6d0882d020
commit e159536ff3

View File

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.ServiceBus; using Microsoft.Azure.ServiceBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
@ -34,283 +35,288 @@ using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Basket.API namespace Microsoft.eShopOnContainers.Services.Basket.API
{ {
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 container. // This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services) public IServiceProvider ConfigureServices(IServiceCollection services)
{ {
RegisterAppInsights(services); RegisterAppInsights(services);
// Add framework services. services.Configure<ApiBehaviorOptions>(opt =>
services.AddMvc(options => {
{ opt.SuppressModelStateInvalidFilter = true;
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); });
options.Filters.Add(typeof(ValidateModelStateFilter));
}) // Add framework services.
.SetCompatibilityVersion(AspNetCore.Mvc.CompatibilityVersion.Version_2_1) services.AddMvc(options =>
.AddControllersAsServices(); {
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
options.Filters.Add(typeof(ValidateModelStateFilter));
ConfigureAuthService(services); })
.SetCompatibilityVersion(AspNetCore.Mvc.CompatibilityVersion.Version_2_1)
.AddControllersAsServices();
services.AddHealthChecks(checks => ConfigureAuthService(services);
{
checks.AddValueTaskCheck("HTTP Endpoint", () => new ValueTask<IHealthCheckResult>(HealthCheckResult.Healthy("Ok")),
TimeSpan.Zero //No cache for this HealthCheck, better just for demos
);
});
services.Configure<BasketSettings>(Configuration); services.AddHealthChecks(checks =>
{
checks.AddValueTaskCheck("HTTP Endpoint", () => new ValueTask<IHealthCheckResult>(HealthCheckResult.Healthy("Ok")),
TimeSpan.Zero //No cache for this HealthCheck, better just for demos
);
});
//By connecting here we are making sure that our service services.Configure<BasketSettings>(Configuration);
//cannot start until redis is ready. This might slow down startup,
//but given that there is a delay on resolving the ip address
//and then creating the connection it seems reasonable to move
//that cost to startup instead of having the first request pay the
//penalty.
services.AddSingleton<ConnectionMultiplexer>(sp =>
{
var settings = sp.GetRequiredService<IOptions<BasketSettings>>().Value;
var configuration = ConfigurationOptions.Parse(settings.ConnectionString, true);
configuration.ResolveDns = true; //By connecting here we are making sure that our service
//cannot start until redis is ready. This might slow down startup,
//but given that there is a delay on resolving the ip address
//and then creating the connection it seems reasonable to move
//that cost to startup instead of having the first request pay the
//penalty.
services.AddSingleton<ConnectionMultiplexer>(sp =>
{
var settings = sp.GetRequiredService<IOptions<BasketSettings>>().Value;
var configuration = ConfigurationOptions.Parse(settings.ConnectionString, true);
return ConnectionMultiplexer.Connect(configuration); configuration.ResolveDns = true;
});
return ConnectionMultiplexer.Connect(configuration);
});
if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) if (Configuration.GetValue<bool>("AzureServiceBusEnabled"))
{ {
services.AddSingleton<IServiceBusPersisterConnection>(sp => services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{ {
var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>(); var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>();
var serviceBusConnectionString = Configuration["EventBusConnection"]; var serviceBusConnectionString = Configuration["EventBusConnection"];
var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString);
return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger);
}); });
} }
else else
{ {
services.AddSingleton<IRabbitMQPersistentConnection>(sp => services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{ {
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory() var factory = new ConnectionFactory()
{ {
HostName = Configuration["EventBusConnection"] HostName = Configuration["EventBusConnection"]
}; };
if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) if (!string.IsNullOrEmpty(Configuration["EventBusUserName"]))
{ {
factory.UserName = Configuration["EventBusUserName"]; factory.UserName = Configuration["EventBusUserName"];
} }
if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) if (!string.IsNullOrEmpty(Configuration["EventBusPassword"]))
{ {
factory.Password = Configuration["EventBusPassword"]; factory.Password = Configuration["EventBusPassword"];
} }
var retryCount = 5; var retryCount = 5;
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"]))
{ {
retryCount = int.Parse(Configuration["EventBusRetryCount"]); retryCount = int.Parse(Configuration["EventBusRetryCount"]);
} }
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
}); });
} }
RegisterEventBus(services); RegisterEventBus(services);
services.AddSwaggerGen(options => services.AddSwaggerGen(options =>
{ {
options.DescribeAllEnumsAsStrings(); options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Info options.SwaggerDoc("v1", new Info
{ {
Title = "Basket HTTP API", Title = "Basket HTTP API",
Version = "v1", Version = "v1",
Description = "The Basket Service HTTP API", Description = "The Basket Service HTTP API",
TermsOfService = "Terms Of Service" TermsOfService = "Terms Of Service"
}); });
options.AddSecurityDefinition("oauth2", new OAuth2Scheme options.AddSecurityDefinition("oauth2", new OAuth2Scheme
{ {
Type = "oauth2", Type = "oauth2",
Flow = "implicit", Flow = "implicit",
AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize", AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token", TokenUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token",
Scopes = new Dictionary<string, string>() Scopes = new Dictionary<string, string>()
{ {
{ "basket", "Basket API" } { "basket", "Basket API" }
} }
}); });
options.OperationFilter<AuthorizeCheckOperationFilter>(); options.OperationFilter<AuthorizeCheckOperationFilter>();
}); });
services.AddCors(options => services.AddCors(options =>
{ {
options.AddPolicy("CorsPolicy", options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin() builder => builder.AllowAnyOrigin()
.AllowAnyMethod() .AllowAnyMethod()
.AllowAnyHeader() .AllowAnyHeader()
.AllowCredentials()); .AllowCredentials());
}); });
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IBasketRepository, RedisBasketRepository>(); services.AddTransient<IBasketRepository, RedisBasketRepository>();
services.AddTransient<IIdentityService, IdentityService>(); services.AddTransient<IIdentityService, IdentityService>();
services.AddOptions(); services.AddOptions();
var container = new ContainerBuilder(); var container = new ContainerBuilder();
container.Populate(services); container.Populate(services);
return new AutofacServiceProvider(container.Build()); return new AutofacServiceProvider(container.Build());
} }
// 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)
{
loggerFactory.AddAzureWebAppDiagnostics();
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"]; // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
if (!string.IsNullOrEmpty(pathBase)) public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ {
app.UsePathBase(pathBase); loggerFactory.AddAzureWebAppDiagnostics();
} loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(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.UseStaticFiles(); app.UseStaticFiles();
app.UseCors("CorsPolicy"); app.UseCors("CorsPolicy");
ConfigureAuth(app); ConfigureAuth(app);
app.UseMvcWithDefaultRoute(); app.UseMvcWithDefaultRoute();
app.UseSwagger() app.UseSwagger()
.UseSwaggerUI(c => .UseSwaggerUI(c =>
{ {
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Basket.API V1"); c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Basket.API V1");
c.OAuthClientId ("basketswaggerui"); c.OAuthClientId("basketswaggerui");
c.OAuthAppName("Basket Swagger UI"); c.OAuthAppName("Basket Swagger UI");
}); });
ConfigureEventBus(app); ConfigureEventBus(app);
} }
private void RegisterAppInsights(IServiceCollection services) private void RegisterAppInsights(IServiceCollection services)
{ {
services.AddApplicationInsightsTelemetry(Configuration); services.AddApplicationInsightsTelemetry(Configuration);
var orchestratorType = Configuration.GetValue<string>("OrchestratorType"); var orchestratorType = Configuration.GetValue<string>("OrchestratorType");
if (orchestratorType?.ToUpper() == "K8S")
{
// Enable K8s telemetry initializer
services.EnableKubernetes();
}
if (orchestratorType?.ToUpper() == "SF")
{
// Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer());
}
}
private void ConfigureAuthService(IServiceCollection services) if (orchestratorType?.ToUpper() == "K8S")
{ {
// prevent from mapping "sub" claim to nameidentifier. // Enable K8s telemetry initializer
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); services.EnableKubernetes();
}
if (orchestratorType?.ToUpper() == "SF")
{
// Enable SF telemetry initializer
services.AddSingleton<ITelemetryInitializer>((serviceProvider) =>
new FabricTelemetryInitializer());
}
}
var identityUrl = Configuration.GetValue<string>("IdentityUrl"); private void ConfigureAuthService(IServiceCollection services)
{
services.AddAuthentication(options => // prevent from mapping "sub" claim to nameidentifier.
{ JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => var identityUrl = Configuration.GetValue<string>("IdentityUrl");
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "basket";
});
}
protected virtual void ConfigureAuth(IApplicationBuilder app) services.AddAuthentication(options =>
{ {
if (Configuration.GetValue<bool>("UseLoadTest")) options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
{ options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
app.UseMiddleware<ByPassAuthMiddleware>();
}
app.UseAuthentication(); }).AddJwtBearer(options =>
} {
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "basket";
});
}
private void RegisterEventBus(IServiceCollection services) protected virtual void ConfigureAuth(IApplicationBuilder app)
{ {
var subscriptionClientName = Configuration["SubscriptionClientName"]; if (Configuration.GetValue<bool>("UseLoadTest"))
{
app.UseMiddleware<ByPassAuthMiddleware>();
}
if (Configuration.GetValue<bool>("AzureServiceBusEnabled")) app.UseAuthentication();
{ }
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
return new EventBusServiceBus(serviceBusPersisterConnection, logger, private void RegisterEventBus(IServiceCollection services)
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); {
}); var subscriptionClientName = Configuration["SubscriptionClientName"];
}
else
{
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp =>
{
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5; if (Configuration.GetValue<bool>("AzureServiceBusEnabled"))
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) {
{ services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
retryCount = int.Parse(Configuration["EventBusRetryCount"]); {
} var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); return new EventBusServiceBus(serviceBusPersisterConnection, logger,
}); eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope);
} });
}
else
{
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp =>
{
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); var retryCount = 5;
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(Configuration["EventBusRetryCount"]);
}
services.AddTransient<ProductPriceChangedIntegrationEventHandler>(); return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
services.AddTransient<OrderStartedIntegrationEventHandler>(); });
} }
private void ConfigureEventBus(IApplicationBuilder app) services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>(); services.AddTransient<ProductPriceChangedIntegrationEventHandler>();
eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>(); services.AddTransient<OrderStartedIntegrationEventHandler>();
} }
}
private void ConfigureEventBus(IApplicationBuilder app)
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>();
eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>();
}
}
} }