Esempio n. 1
0
        protected new UnprocessableEntityObjectResult UnprocessableEntity()
        {
            var validationProblemDetails = ValidationProblemDetailsFactory.Create(ControllerContext);

            validationProblemDetails.Status = StatusCodes.Status422UnprocessableEntity;
            return(UnprocessableEntity(validationProblemDetails));
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
        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);
        }
Esempio n. 5
0
        // 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"]);
        }
Esempio n. 6
0
 // 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"));
     });
 }