示例#1
0
        public static IServiceCollection ConfigureDefaultForApi <T, TSharedResources>(
            this IServiceCollection services,
            StartupConfigureOptions options)
        {
            if (options.Swagger.ApiInfo == null)
            {
                throw new ArgumentNullException(nameof(options.Swagger.ApiInfo));
            }

            var configuredCorsMethods = new[]
            {
                HttpMethod.Get.Method,
                HttpMethod.Head.Method,
                HttpMethod.Post.Method,
                HttpMethod.Put.Method,
                HttpMethod.Patch.Method,
                HttpMethod.Delete.Method,
                HttpMethod.Options.Method
            }.Union(options.Cors.Methods ?? Array.Empty <string>()).Distinct().ToArray();

            var configuredCorsHeaders = new[]
            {
                HeaderNames.Accept,
                HeaderNames.ContentType,
                HeaderNames.Origin,
                HeaderNames.Authorization,
                HeaderNames.IfMatch,
                ExtractFilteringRequestExtension.HeaderName,
                AddSortingExtension.HeaderName,
                AddPaginationExtension.HeaderName
            }.Union(options.Cors.Headers ?? Array.Empty <string>()).Distinct().ToArray();

            var configuredCorsExposedHeaders = new[]
            {
                HeaderNames.Location,
                ExtractFilteringRequestExtension.HeaderName,
                AddSortingExtension.HeaderName,
                AddPaginationExtension.HeaderName,
                options.Server.VersionHeaderName,
                AddCorrelationIdToResponseMiddleware.HeaderName,
                AddHttpSecurityHeadersMiddleware.PoweredByHeaderName,
                AddHttpSecurityHeadersMiddleware.ContentTypeOptionsHeaderName,
                AddHttpSecurityHeadersMiddleware.FrameOptionsHeaderName,
                AddHttpSecurityHeadersMiddleware.XssProtectionHeaderName,
                AddVersionHeaderMiddleware.HeaderName
            }.Union(options.Cors.ExposedHeaders ?? Array.Empty <string>()).Distinct().ToArray();

            services.AddSingleton(options);

            services.TryAddEnumerable(ServiceDescriptor.Transient <IApiControllerSpecification, ApiControllerSpec>());

            services
            .AddHttpContextAccessor()

            .ConfigureOptions <ProblemDetailsSetup>()
            .AddProblemDetails(cfg =>
            {
                foreach (var header in configuredCorsExposedHeaders)
                {
                    if (!cfg.AllowedHeaderNames.Contains(header))
                    {
                        cfg.AllowedHeaderNames.Add(header);
                    }
                }

                options.MiddlewareHooks.ConfigureProblemDetails?.Invoke(cfg);
            });

            var mvcBuilder = services
                             .AddMvcCore(cfg =>
            {
                cfg.RespectBrowserAcceptHeader = false;
                cfg.ReturnHttpNotAcceptable    = true;

                cfg.Filters.Add(new LoggingFilterFactory(options.Server.MethodsToLog));

                // This got removed in .NET Core 3.0, we need to determine the impact
                //cfg.Filters.Add(new CorsAuthorizationFilterFactory(StartupHelpers.AllowSpecificOrigin));

                cfg.Filters.Add <OperationCancelledExceptionFilter>();

                cfg.Filters.Add(new DataDogTracingFilter());

                cfg.EnableEndpointRouting = false;

                options.MiddlewareHooks.ConfigureMvcCore?.Invoke(cfg);
            });

            options.MiddlewareHooks.AfterMvcCore?.Invoke(mvcBuilder);

            mvcBuilder
            .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)

            .AddDataAnnotationsLocalization(options.MiddlewareHooks.DataAnnotationsLocalization)

            .AddFluentValidation(
                options.MiddlewareHooks.FluentValidation
                ?? (fv => fv.RegisterValidatorsFromAssemblyContaining <T>()))

            .AddCors(cfg =>
            {
                cfg.AddPolicy(StartupHelpers.AllowAnyOrigin, corsPolicy => corsPolicy
                              .AllowAnyOrigin()
                              .WithMethods(configuredCorsMethods)
                              .WithHeaders(configuredCorsHeaders)
                              .WithExposedHeaders(configuredCorsExposedHeaders)
                              .SetPreflightMaxAge(TimeSpan.FromSeconds(60 * 15)));

                cfg.AddPolicy(StartupHelpers.AllowSpecificOrigin, corsPolicy => corsPolicy
                              .WithOrigins(options.Cors.Origins ?? new string[0])
                              .WithMethods(configuredCorsMethods)
                              .WithHeaders(configuredCorsHeaders)
                              .WithExposedHeaders(configuredCorsExposedHeaders)
                              .SetPreflightMaxAge(TimeSpan.FromSeconds(60 * 15))
                              .AllowCredentials());

                options.MiddlewareHooks.ConfigureCors?.Invoke(cfg);
            })

            .AddControllersAsServices()
            .AddAuthorization(options.MiddlewareHooks.Authorization)

            .AddNewtonsoftJson(
                options.MiddlewareHooks.ConfigureJsonOptions
                ?? (cfg => cfg.SerializerSettings.ConfigureDefaultForApi()))

            .AddXmlDataContractSerializerFormatters()

            .AddFormatterMappings(options.MiddlewareHooks.ConfigureFormatterMappings)

            .AddApiExplorer();

            options.MiddlewareHooks.AfterMvc?.Invoke(mvcBuilder);

            var healthChecksBuilder = services.AddHealthChecks();

            options.MiddlewareHooks.AfterHealthChecks?.Invoke(healthChecksBuilder);

            services
            .AddLocalization(cfg => cfg.ResourcesPath = "Resources")
            .AddSingleton <IStringLocalizerFactory, SharedStringLocalizerFactory <TSharedResources> >()
            .AddSingleton <ResourceManagerStringLocalizerFactory, ResourceManagerStringLocalizerFactory>()

            .Configure <RequestLocalizationOptions>(opts =>
            {
                const string fallbackCulture   = "en-GB";
                var defaultRequestCulture      = new RequestCulture(options.Localization.DefaultCulture ?? new CultureInfo(fallbackCulture));
                var supportedCulturesOrDefault = options.Localization.SupportedCultures ?? new[] { new CultureInfo(fallbackCulture) };

                opts.DefaultRequestCulture = defaultRequestCulture;
                opts.SupportedCultures     = supportedCulturesOrDefault;
                opts.SupportedUICultures   = supportedCulturesOrDefault;

                opts.FallBackToParentCultures   = true;
                opts.FallBackToParentUICultures = true;
            })

            .AddVersionedApiExplorer(cfg =>
            {
                cfg.GroupNameFormat           = "'v'VVV";
                cfg.SubstituteApiVersionInUrl = true;
            })

            .AddApiVersioning(cfg =>
            {
                cfg.ReportApiVersions = true;
                cfg.ErrorResponses    = new ProblemDetailsResponseProvider();
            })

            .AddSwagger <T>(new SwaggerOptions
            {
                ApiInfoFunc     = options.Swagger.ApiInfo,
                XmlCommentPaths = options.Swagger.XmlCommentPaths ?? new string[0],
                AdditionalHeaderOperationFilters = options.Swagger.AdditionalHeaderOperationFilters,
                Servers         = options.Swagger.Servers,
                CustomSortFunc  = options.Swagger.CustomSortFunc,
                MiddlewareHooks =
                {
                    AfterSwaggerGen = options.Swagger.MiddlewareHooks.AfterSwaggerGen
                }
            })

            .AddResponseCompression(cfg =>
            {
                cfg.EnableForHttps = true;

                cfg.Providers.Add <BrotliCompressionProvider>();
                cfg.Providers.Add <GzipCompressionProvider>();

                cfg.MimeTypes = new[]
                {
                    // General
                    "text/plain",
                    "text/csv",

                    // Static files
                    "text/css",
                    "application/javascript",

                    // MVC
                    "text/html",
                    "application/xml",
                    "text/xml",
                    "application/json",
                    "text/json",
                    "application/ld+json",
                    "application/atom+xml",

                    // Fonts
                    "application/font-woff",
                    "font/otf",
                    "application/vnd.ms-fontobject"
                };
            })
            .Configure <GzipCompressionProviderOptions>(cfg => cfg.Level   = CompressionLevel.Fastest)
            .Configure <BrotliCompressionProviderOptions>(cfg => cfg.Level = CompressionLevel.Fastest)
            .Configure <KestrelServerOptions>(serverOptions => serverOptions.AllowSynchronousIO = true);

            ValidatorOptions.DisplayNameResolver =
                (type, member, expression) =>
                member != null
                        ? GlobalStringLocalizer.Instance.GetLocalizer <TSharedResources>().GetString(() => member.Name)
                        : null;

            return(services);
        }
示例#2
0
 public static IServiceCollection ConfigureDefaultForApi <T>(
     this IServiceCollection services,
     StartupConfigureOptions options) =>
 services.ConfigureDefaultForApi <T, DefaultResources>(options);