protected new UnprocessableEntityObjectResult UnprocessableEntity() { var validationProblemDetails = ValidationProblemDetailsFactory.Create(ControllerContext); validationProblemDetails.Status = StatusCodes.Status422UnprocessableEntity; return(UnprocessableEntity(validationProblemDetails)); }
private static async Task HandleEntityNotFoundException(HttpContext context, Exception ex) { var problemDetails = ValidationProblemDetailsFactory.CreateNotFoundProblemDetails(context, ex.Message); var result = JsonConvert.SerializeObject(problemDetails); context.Response.ContentType = "application/problem+json"; context.Response.StatusCode = StatusCodes.Status404NotFound; await context.Response.WriteAsync(result); }
private async Task HandleBusinessException(HttpContext context, Exception ex) { var validationProblemDetails = ValidationProblemDetailsFactory .Create(context, new Dictionary <string, string[]> { { "", new string[] { ex.Message } } }); var result = JsonConvert.SerializeObject(validationProblemDetails); context.Response.ContentType = "application/problem+json"; context.Response.StatusCode = context.Request.Method == HttpMethods.Delete ? StatusCodes.Status409Conflict : StatusCodes.Status422UnprocessableEntity; await context.Response.WriteAsync(result); }
private async Task HandleException(HttpContext context, Exception ex) { var innermostException = GetInnermostException(ex); var problemDetail = ValidationProblemDetailsFactory.CreateInternalServerErrorProblemDetails(context, innermostException.Message); _options.ApiErrorHandler?.Invoke(context, ex, problemDetail); var logLevel = _options.LogLevelHandler?.Invoke(context, ex) ?? LogLevel.Error; if (context.Request.Method == "POST" || context.Request.Method == "PUT") { string body = string.Empty; using (var reader = new StreamReader(context.Request.Body)) { context.Request.Body.Seek(0, SeekOrigin.Begin); body = await reader.ReadToEndAsync(); } _logger.Log( logLevel, innermostException, innermostException.Message + " -- {TraceId} -- {Body}", problemDetail.Extensions["traceId"], body); } else { _logger.Log( logLevel, innermostException, innermostException.Message + " -- {TraceId}", problemDetail.Extensions["traceId"]); } var result = JsonConvert.SerializeObject(problemDetail); context.Response.ContentType = "application/problem+json"; context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; await context.Response.WriteAsync(result); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services .AddControllers(configure => { configure.ReturnHttpNotAcceptable = true; configure.Filters.Add(new ProducesResponseTypeAttribute(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)); configure.Filters.Add(new ProducesResponseTypeAttribute(typeof(ProblemDetails), StatusCodes.Status404NotFound)); configure.Filters.Add(new ProducesResponseTypeAttribute(typeof(object), StatusCodes.Status406NotAcceptable)); configure.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status500InternalServerError)); configure.Filters.Add(typeof(LoggingFilter)); }) .ConfigureApiBehaviorOptions(options => { options.InvalidModelStateResponseFactory = actionContext => { var actionExecutingContext = actionContext as ActionExecutingContext; var validationProblemDetails = ValidationProblemDetailsFactory.Create(actionContext); if (actionContext.ModelState.ErrorCount > 0 && actionExecutingContext?.ActionArguments.Count == actionContext.ActionDescriptor.Parameters.Count) { validationProblemDetails.Status = StatusCodes.Status422UnprocessableEntity; return(new UnprocessableEntityObjectResult(validationProblemDetails)); } validationProblemDetails.Status = StatusCodes.Status400BadRequest; return(new BadRequestObjectResult(validationProblemDetails)); }; }) .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining <Startup>()); services.AddDbContext <StackUnderflowDbContext>(options => { options.UseNpgsql(_configuration.GetConnectionString("StackUnderflowDbConnection")); if (_hostEnvironment.IsDevelopment()) { options.EnableSensitiveDataLogging(true); } }); services.AddGenericRepository(); services.AddSpecificRepositories(); services.AddCoreServices(); services.AddPropertyMappingService(opts => opts.PropertyMappings = new List <IPropertyMapping> { new PropertyMapping <QuestionSummaryGetViewModel, QuestionSummaryGetModel>() .Add(nameof(QuestionSummaryGetViewModel.Username), $"{nameof(User)}.{nameof(User.Username)}") .Add(nameof(QuestionSummaryGetViewModel.HasAcceptedAnswer), nameof(QuestionSummaryGetModel.HasAcceptedAnswer)) .Add(nameof(QuestionSummaryGetViewModel.CreatedOn), nameof(QuestionSummaryGetModel.CreatedOn)) .Add(nameof(QuestionSummaryGetViewModel.VotesSum), nameof(QuestionSummaryGetModel.VotesSum)), new PropertyMapping <AnswerGetViewModel, AnswerGetModel>() .Add(nameof(AnswerGetViewModel.CreatedOn), nameof(AnswerGetModel.CreatedOn)) .Add(nameof(AnswerGetViewModel.VotesSum), nameof(AnswerGetModel.VotesSum)) }); services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton <IScopeInformation, ScopeInformation>(); services.AddAutoMapper(Assembly.GetExecutingAssembly(), typeof(CommentProfile).Assembly); services.AddSingleton <ICache, Cache>(); services.AddMemoryCache(); services.AddSwaggerGen(setupAction => { setupAction.SwaggerDoc( "StackUnderflowOpenAPISpecification", new OpenApiInfo { Title = "Stack Underflow API", Version = "v1", Description = "This API allows access to the Stack Underflow Q&A.", Contact = new OpenApiContact { Name = "Nenad Livaic", Url = new Uri("https://github.com/nlivaic") }, License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://www.opensource.org/licenses/MIT") }, TermsOfService = new Uri("https://www.my-terms-of-service.com") }); // A workaround for having multiple POST methods on one controller. // setupAction.ResolveConflictingActions(r => r.First()); setupAction.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "StackUnderflow.Api.xml")); }); // Commented out as we are running front end as a standalone app. // services.AddSpaStaticFiles(configuration => // { // configuration.RootPath = "ClientApp/build"; // }); services.AddCors(o => o.AddPolicy("All", builder => { builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() .WithExposedHeaders(Constants.Headers.Pagination); })); services.AddCors(o => o.AddPolicy("StackUnderflowClient", builder => { var allowedOrigins = _configuration["AllowedOrigins"]?.Split(',') ?? new string[0]; builder .WithOrigins(allowedOrigins) .WithHeaders("Authorization", "Content-Type") .WithExposedHeaders(Constants.Headers.Pagination) .WithMethods(HttpMethods.Get, HttpMethods.Post, HttpMethods.Put, HttpMethods.Delete); })); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddIdentityServerAuthentication("Bearer", options => { options.Authority = _configuration["IdP:Authority"]; // Our IDP. Middleware uses this to know where to find public keys and endpoints. options.ApiName = _configuration["IdP:ApiName"]; // Allows the access token validator to check if the access token `audience` is for this API. }); services.AddAuthorization(); services.AddApiEventPublisher(_configuration["CONNECTIONSTRINGS:MESSAGEBROKER:WRITE"]); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddTransient <IDataShapingService, DataShapingService>(); services.AddSingleton <IPagingService, PagingService>(); services.AddTransient <IPropertyMappingService, PropertyMappingService>(); services .AddControllers(configure => { configure.ReturnHttpNotAcceptable = true; configure.CacheProfiles.Add( "240SecondsCacheProfile", new CacheProfile { Duration = 240 }); configure.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status400BadRequest)); configure.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status406NotAcceptable)); configure.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status500InternalServerError)); }) .AddNewtonsoftJson() .AddXmlDataContractSerializerFormatters() .ConfigureApiBehaviorOptions(options => { options.InvalidModelStateResponseFactory = actionContext => { var actionExecutingContext = actionContext as ActionExecutingContext; var validationProblemDetails = ValidationProblemDetailsFactory.Create(actionContext); if (actionContext.ModelState.ErrorCount > 0 && actionExecutingContext?.ActionArguments.Count == actionContext.ActionDescriptor.Parameters.Count) { validationProblemDetails.Status = StatusCodes.Status422UnprocessableEntity; return(new UnprocessableEntityObjectResult(validationProblemDetails)); } validationProblemDetails.Status = StatusCodes.Status400BadRequest; return(new BadRequestObjectResult(validationProblemDetails)); }; }); services.AddHttpCacheHeaders( expirationOptions => { expirationOptions.MaxAge = 120; }, validationOptions => { validationOptions.MustRevalidate = true; } ); // services.AddResponseCaching(); services.AddDbContext <CourseLibraryContext>(options => options.UseNpgsql(Configuration.GetConnectionString("CourseLibraryDatabase"))); services.AddScoped <ICourseLibraryRepository, CourseLibraryRepository>(); services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); services.AddSwaggerGen(setupAction => { setupAction.SwaggerDoc( "LibraryOpenAPISpecification", new OpenApiInfo { Title = "Library API", Version = "v1", Description = "This API allows access to authors and their books.", Contact = new OpenApiContact { Name = "My name", Url = new Uri("https://www.somewhere.com") }, License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://www.opensource.org/licenses/MIT") }, TermsOfService = new Uri("https://www.your-terms-of-service.com") }); // A workaround for having multiple POST methods on one controller. // setupAction.ResolveConflictingActions(r => r.First()); setupAction.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "RestfulAPICore3.API.xml")); }); }