public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); } app.UseExceptionHandler(errorApp => { errorApp.Run(context => { string InstanceId() => $"urn:backoffice:log:{Guid.NewGuid()}"; var errorFeature = context.Features.Get <IExceptionHandlerFeature>(); var exception = errorFeature.Error; if (exception is ValidationException validationException) { var problemDetails = new CustomValidationProblemDetails <ValidationError> { Status = StatusCodes.Status422UnprocessableEntity, Instance = InstanceId(), Details = validationException.Failures.Select(e => new ValidationError { Field = e.Key, Errors = e.Value }).ToList() }; context.Response.StatusCode = problemDetails.Status.GetValueOrDefault(); context.Response.WriteJson(problemDetails, "application/problem+json"); } else if (exception is BusinessException backOfficeException) { var problemDetails = new CustomValidationProblemDetails <string> { Status = StatusCodes.Status422UnprocessableEntity, Details = new [] { backOfficeException.Message }, Instance = InstanceId() }; context.Response.StatusCode = problemDetails.Status.GetValueOrDefault(); context.Response.WriteJson(problemDetails, "application/problem+json"); } else if (exception is NotFoundException) { var problemDetails = new CustomValidationProblemDetails <string> { Status = StatusCodes.Status404NotFound, Instance = InstanceId() }; context.Response.StatusCode = problemDetails.Status.GetValueOrDefault(); context.Response.WriteJson(problemDetails, "application/problem+json"); } else { var errorDetail = Environment.IsDevelopment() ? exception.Demystify().ToString() : "The instance value should be used to identify the problem when calling customer support"; var problemDetails = new ProblemDetails { Title = "An unexpected error occurred!", Status = StatusCodes.Status500InternalServerError, Detail = errorDetail, Instance = InstanceId() }; context.Response.StatusCode = problemDetails.Status.GetValueOrDefault(); context.Response.WriteJson(problemDetails, "application/problem+json"); } // log the exception etc.. return(Task.CompletedTask); }); }); app.UseMiniProfiler(); app.UseRequestLocalization(options => { var supportedCultures = new List <CultureInfo> { new CultureInfo("pt-BR"), new CultureInfo("en-US") }; options.DefaultRequestCulture = new RequestCulture("pt-BR"); // Formatting numbers, dates, etc. options.SupportedCultures = supportedCultures; // UI strings that we have localized. options.SupportedUICultures = supportedCultures; }); app.UseMvc(); app.UseOpenSpecificationApi(); }
// This method gets called by the runtime. Use this method to add services to the container. public IServiceProvider ConfigureServices(IServiceCollection services) { // Disable JWT inbound claim name mapping. This behaviour by default maps JWT claim names to their much longer counterparts. // See https://mderriey.com/2019/06/23/where-are-my-jwt-claims/ JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // Add validation of antiforgery tokens for unsave methods. // See https://docs.microsoft.com/pl-pl/dotnet/api/microsoft.aspnetcore.mvc.autovalidateantiforgerytokenattribute?view=aspnetcore-2.2 services.AddMvc(options => { //options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2) .AddFluentValidation() .ConfigureApiBehaviorOptions(config => { config.InvalidModelStateResponseFactory = context => { // Add custom validation error response. var validationError = new CustomValidationProblemDetails(context); return(new ObjectResult(validationError)); }; }) .AddJsonOptions(options => { // Ignore reference loops in JSON responses. options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // Convert each enum to its string representation. options.SerializerSettings.Converters.Add(new StringEnumConverter()); // Set DateTime format in JSON, eg. "2019-12-12T23:12:56Z" options.SerializerSettings.DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ssK"; }); // In production, the Angular files will be served from this directory. services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/dist"; }); // Add default CORS policy. services.AddCors(config => { config.AddPolicy(ApiConstants.DefaultCorsPolicy, policy => { policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials().Build(); }); }); // Set DbContext for app. services.AddDbContext <ApplicationDbContext>(options => options.UseSqlServer(GetConnectionString(ApiConstants.DefaultConnectionString))); // Automatically perform database migration. //services.BuildServiceProvider().GetService<ApplicationDbContext>().Database.Migrate(); // Set user requirements. services.AddIdentity <IdentityUser, IdentityRole>(config => { config.Password.RequireDigit = true; config.Password.RequiredLength = 8; config.Password.RequireLowercase = true; config.Password.RequireNonAlphanumeric = true; config.Password.RequireUppercase = true; config.Lockout.AllowedForNewUsers = true; config.Lockout.MaxFailedAccessAttempts = 3; config.User.RequireUniqueEmail = true; config.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz"; config.SignIn.RequireConfirmedEmail = false; }).AddEntityFrameworkStores <ApplicationDbContext>(); // Get JWT settings. var jwtSettings = GetJwtSettings(services); // Add JWT Bearer authentication. services.AddAuthentication(config => { config.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; config.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; config.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(config => { config.TokenValidationParameters = jwtSettings.GetValidationParameters(); config.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { // Set the 'Token-Expired: true' header for each request that has provided an expired access token. if (context.Exception is SecurityTokenExpiredException) { context.Response.Headers.Add("Token-Expired", "true"); } return(Task.CompletedTask); } }; }); // Add authorization policies. services.AddAuthorization(options => { options.AddPolicy(ApiConstants.ApiAdminPolicy, config => config.RequireAuthenticatedUser().RequireClaim(ApiConstants.RoleClaim, ApiConstants.AdministratorRole)); options.AddPolicy(ApiConstants.ApiUserPolicy, config => config.RequireAuthenticatedUser().RequireClaim(ApiConstants.RoleClaim, ApiConstants.AdministratorRole, ApiConstants.ModeratorRole)); }); // Add AutoMapper. services.AddAutoMapper(Assembly.GetExecutingAssembly().GetTypes()); // Replacement of built-in service container with Autofac. var containerBuilder = new ContainerBuilder(); containerBuilder.RegisterModule <ApplicationModule>(); containerBuilder.Populate(services); var container = containerBuilder.Build(); return(new AutofacServiceProvider(container)); }