// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IApiVersionDescriptionProvider provider) { app.UseDefaultFiles(); var cultures = Configuration.GetSection("SupportedCultures").Get <string[]>(); var supportedCultures = new List <CultureInfo>(); foreach (var culture in cultures) { supportedCultures.Add(new CultureInfo(culture)); } app.UseRequestLocalization(new RequestLocalizationOptions { DefaultRequestCulture = new RequestCulture("en"), SupportedCultures = supportedCultures, SupportedUICultures = supportedCultures }); env.EnvironmentName = Environments.Development; if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor, // IIS is also tagging a X-Forwarded-For header on, so we need to increase this limit, // otherwise the X-Forwarded-For we are passing along from the browser will be ignored ForwardLimit = 2 }); } if (!Directory.Exists("Logs")) { Directory.CreateDirectory("Logs"); } var webSocketOptions = new WebSocketOptions() { KeepAliveInterval = TimeSpan.FromSeconds(5), ReceiveBufferSize = 4 * 1024 }; app.UseWebSockets(webSocketOptions); // Enable middleware to serve generated Swagger as a JSON endpoint. app.UseSwagger(options => { options.PreSerializeFilters.Add((swagger, httpReq) => { //swagger.Host = httpReq.Host.Value; var ampersand = "&"; foreach (var path in swagger.Paths) { if (path.Value.Operations.Any(x => x.Key == OperationType.Get && x.Value.Deprecated)) { path.Value.Operations.First(x => x.Key == OperationType.Get).Value.Description = path.Value.Operations.First(x => x.Key == OperationType.Get).Value.Description.Replace(ampersand, "&"); } if (path.Value.Operations.Any(x => x.Key == OperationType.Delete && x.Value?.Description != null)) { path.Value.Operations.First(x => x.Key == OperationType.Delete).Value.Description = path.Value.Operations.First(x => x.Key == OperationType.Delete).Value.Description.Replace(ampersand, "&"); } } var paths = swagger.Paths.ToDictionary(p => p.Key, p => p.Value); foreach (KeyValuePair <string, OpenApiPathItem> path in paths) { swagger.Paths.Remove(path.Key); swagger.Paths.Add(path.Key.ToLowerInvariant(), path.Value); } }); }); // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint. app.UseSwaggerUI(options => { options.IndexStream = () => File.OpenRead("Views/Swagger/swagger-ui.html"); foreach (var description in provider.ApiVersionDescriptions) { options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); } options.EnableFilter(); }); app.UseCors(builder => builder.WithOrigins("http://localhost:4200").AllowAnyHeader().AllowAnyMethod()); app.UseStaticFiles(); app.UseRouting(); #region Error handler // Different middleware for api and ui requests app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), appBuilder => { var localizer = serviceProvider.GetService <IStringLocalizer <ErrorsResource> >(); var logger = loggerFactory.CreateLogger("GlobalErrorHandling"); // Exception handler - show exception data in api response appBuilder.UseExceptionHandler(new ExceptionHandlerOptions { ExceptionHandler = async context => { var errorModel = new ErrorResponseModel(localizer); var result = new ContentResult(); var exception = context.Features.Get <IExceptionHandlerPathFeature>(); if (exception.Error is CustomException) { var ex = (CustomException)exception.Error; result = errorModel.Error(ex); } else { var message = exception.Error.InnerException?.Message ?? exception.Error.Message; logger.LogError($"{exception.Path} - {message}"); errorModel.AddError("general", message); result = errorModel.InternalServerError(env.IsDevelopment() ? exception.Error.StackTrace : null); } context.Response.StatusCode = result.StatusCode.Value; context.Response.ContentType = result.ContentType; await context.Response.WriteAsync(result.Content); } }); // Handles responses with status codes (correctly executed requests, without any exceptions) appBuilder.UseStatusCodePages(async context => { string message = ""; List <ErrorKeyValue> errors = new List <ErrorKeyValue>(); switch (context.HttpContext.Response.StatusCode) { case 400: message = "Bad Request"; break; case 401: message = "Unauthorized"; errors.Add(new ErrorKeyValue("token", "Token invalid")); break; case 403: message = "Forbidden"; break; case 404: message = "Not found"; break; case 500: message = "Internal Server Error"; break; } context.HttpContext.Response.ContentType = "application/json"; await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(new ErrorResponseModel(localizer) { Code = message, StackTrace = "", Errors = errors }, new JsonSerializerSettings { Formatting = Formatting.Indented })); }); }); app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"), appBuilder => { appBuilder.UseExceptionHandler("/Error"); appBuilder.UseStatusCodePagesWithReExecute("/Error", "?statusCode={0}"); }); #endregion app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id}"); }); }
public override void OnActionExecuting(ActionExecutingContext context) { // Apply this logic only if action have one reference type parameter if (context.ActionDescriptor.Parameters.Count > 0 && context.ActionDescriptor.Parameters.Any(x => x.ParameterType.IsClass)) { if (_errorsLocalizer == null) { using (var scope = context.HttpContext.RequestServices.CreateScope()) { _errorsLocalizer = scope.ServiceProvider.GetRequiredService <IStringLocalizer <ErrorsResource> >(); } } var actionData = context.ActionDescriptor.GetType().GetMethod(context.ActionDescriptor.DisplayName); // Return bad request if model is null or non of argument if value type if (!context.ActionArguments.Any()) { context.Result = new ContentResult { Content = JsonConvert.SerializeObject(new ErrorResponseModel(_errorsLocalizer) { Code = ErrorCode.BadRequest, Errors = new List <ErrorKeyValue> { new ErrorKeyValue("model", "Model is invalid") } }), StatusCode = (int)HttpStatusCode.BadRequest, ContentType = "application/json" }; return; } else if (context.ActionArguments.Count != context.ActionDescriptor.Parameters.Count) { // In case if one of argument empty (it can not be bind) and not optional return error with this model var data = context.ActionDescriptor.Parameters.Where(x => x.ParameterType.IsClass && !((ControllerParameterDescriptor)x).ParameterInfo.IsOptional && !context.ActionArguments.ContainsKey(x.Name)).ToList(); if (data.Any()) { _errors = new ErrorResponseModel(_errorsLocalizer); foreach (var x in data) { _errors.AddError(x.Name, $"{x.Name} is invalid"); } context.Result = _errors.BadRequest(); return; } } } // Build validation errors response if (!context.ModelState.IsValid) { _errors = new ErrorResponseModel(_errorsLocalizer); _errors.BuildErrors(context.ModelState); context.Result = _errors.BadRequest(); } return; }