/// <summary>Processes the HTTP request method.</summary> /// <param name="context">The context.</param> /// <param name="routes">The routes.</param> /// <param name="resolver">The resolver.</param> /// <param name="defaultRequestConfiguration">The default request configuration.</param> /// <returns></returns> internal static async Task <bool> ProcessHttpRequestMethod(this ApiRequestContext context, IApiRoutingTable routes, IUriRouteResolver resolver, IDeepSleepRequestConfiguration defaultRequestConfiguration) { if (!context.RequestAborted.IsCancellationRequested) { // Templates exist for thies route if ((context.Routing?.Template?.Locations?.Count ?? 0) > 0) { // A route was not found for the template if (context.Routing.Route == null) { var methods = context.Routing.Template.Locations .Where(e => !string.IsNullOrWhiteSpace(e?.HttpMethod)) .Select(e => e.HttpMethod.ToUpper()) .Distinct() .ToList(); if (methods.Contains("GET") && !methods.Contains("HEAD")) { if (resolver != null) { var match = await resolver.MatchRoute( routes, "GET", context.Request.Path).ConfigureAwait(false); if (match != null) { var enableHeadForGetRequests = match.Configuration?.EnableHeadForGetRequests ?? defaultRequestConfiguration?.EnableHeadForGetRequests ?? ApiRequestContext.GetDefaultRequestConfiguration().EnableHeadForGetRequests ?? true; if (enableHeadForGetRequests) { methods.Add("HEAD"); } } } } context.Runtime.Internals.IsMethodNotFound = true; context.Response.AddHeader("Allow", string.Join(", ", methods)); context.Response.StatusCode = 405; return(false); } } return(true); } return(false); }
/// <summary>Uses the deep sleep services.</summary> /// <param name="services">The services.</param> /// <param name="configure">The configure.</param> /// <returns></returns> public static IServiceCollection UseDeepSleepServices(this IServiceCollection services, Action <IDeepSleepServiceConfiguration> configure = null) { var configuration = new DeepSleepServiceConfiguration { DefaultRequestConfiguration = ApiRequestContext.GetDefaultRequestConfiguration(), ExcludePaths = new List <string>(), IncludePaths = new List <string> { ApiPaths.All() }, WriteConsoleHeader = true, DiscoveryStrategies = new List <IDeepSleepDiscoveryStrategy>() }; if (configure != null) { configure(configuration); } configuration.DefaultRequestConfiguration = configuration.DefaultRequestConfiguration ?? ApiRequestContext.GetDefaultRequestConfiguration(); configuration.ExcludePaths = configuration.ExcludePaths ?? new List <string>(); configuration.IncludePaths = configuration.IncludePaths ?? new List <string>(); var routingTable = new ApiRoutingTable(routePrefix: configuration.RoutePrefix); services .AddScoped <IApiRequestContextResolver, ApiRequestContextResolver>() .AddScoped <IFormUrlEncodedObjectSerializer, FormUrlEncodedObjectSerializer>() .AddScoped <IUriRouteResolver, ApiRouteResolver>() .AddScoped <IMultipartStreamReader, MultipartStreamReader>() .AddScoped <IDeepSleepMediaSerializerFactory, DeepSleepMediaSerializerWriterFactory>() .AddScoped <IApiValidationProvider, ApiEndpointValidationProvider>() .AddSingleton <IApiRequestPipeline, IApiRequestPipeline>((p) => ApiRequestPipeline.GetDefaultRequestPipeline()) .AddSingleton <IDeepSleepRequestConfiguration, IDeepSleepRequestConfiguration>((p) => configuration.DefaultRequestConfiguration) .AddSingleton <IDeepSleepServiceConfiguration, IDeepSleepServiceConfiguration>((p) => configuration) .AddSingleton <IApiRoutingTable, IApiRoutingTable>((p) => routingTable); return(services); }
/// <summary>Processes the HTTP response caching.</summary> /// <param name="context">The context.</param> /// <param name="defaultRequestConfiguration">The default request configuration.</param> /// <returns></returns> internal static Task <bool> ProcessHttpResponseCaching(this ApiRequestContext context, IDeepSleepRequestConfiguration defaultRequestConfiguration) { if (!context.RequestAborted.IsCancellationRequested) { var statusCode = context.Response.StatusCode; var systemConfiguration = ApiRequestContext.GetDefaultRequestConfiguration(); var expirationSeconds = context.Configuration?.CacheDirective?.ExpirationSeconds ?? defaultRequestConfiguration?.CacheDirective?.ExpirationSeconds ?? systemConfiguration.CacheDirective.ExpirationSeconds.Value; var cacheability = context.Configuration?.CacheDirective?.Cacheability ?? defaultRequestConfiguration?.CacheDirective?.Cacheability ?? systemConfiguration.CacheDirective.Cacheability.Value; var cacheLocation = context.Configuration?.CacheDirective?.CacheLocation ?? defaultRequestConfiguration?.CacheDirective?.CacheLocation ?? systemConfiguration.CacheDirective.CacheLocation.Value; var vary = context.Configuration?.CacheDirective?.VaryHeaderValue ?? defaultRequestConfiguration?.CacheDirective?.VaryHeaderValue ?? systemConfiguration.CacheDirective.VaryHeaderValue; if (statusCode >= 200 && statusCode <= 299 && statusCode != 204) { // Don't add cache headers for pre-flight requests // This is handled in the prflight pipeline component if (context.Request.IsCorsPreflightRequest()) { return(Task.FromResult(true)); } if (cacheability == HttpCacheType.Cacheable) { var maxAgeSeconds = expirationSeconds < 0 ? 0 : expirationSeconds; context.Response.AddHeader("Cache-Control", $"{cacheLocation.ToString().ToLower()}, max-age={maxAgeSeconds}"); context.Response.AddHeader("Expires", DateTime.UtcNow.AddSeconds(expirationSeconds).ToString("r")); if (!string.IsNullOrWhiteSpace(vary)) { // ADDING VARY HEADERS TO SPECIFY WHAT THE RESPONSE REPRESENTATION WAS GENERATED AGAINST. context.Response.AddHeader( name: "Vary", value: vary, append: true); } return(Task.FromResult(true)); } } var seconds = expirationSeconds > 0 ? -1 : expirationSeconds; context.Response.AddHeader("Cache-Control", $"no-store, max-age=0"); // this gets updated when the response date is added to the headers. THe value will // ultimately be the response date - expiration seconds. Needs to be here though because the header is checked // for prior to updating it. context.Response.AddHeader("Expires", DateTime.UtcNow.AddSeconds(seconds).ToString("r")); } return(Task.FromResult(true)); }
/// <summary>Writes the deepsleep to console.</summary> /// <param name="routingTable">The routing table.</param> /// <param name="defaultConfiguration">The default configuration.</param> internal static void WriteDeepSleepToConsole(IApiRoutingTable routingTable, IDeepSleepRequestConfiguration defaultConfiguration) { var systemConfiguration = ApiRequestContext.GetDefaultRequestConfiguration(); var existingColor = Console.ForegroundColor; var deepSleepAssemblyInfo = GetAssemplyInfo(typeof(ApiRequestContext)); var deepSleepWebAssemblyInfo = GetAssemplyInfo(typeof(ApiCoreHttpExtensionMethods)); var deepSleepOpenApiAssemblyInfo = GetAssemplyInfo(Type.GetType("DeepSleep.OpenApi.DeepSleepOasGenerator, deepsleep.openapi", false, false)); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($@""); Console.WriteLine($@" ____ _ "); Console.WriteLine($@"| \ ___ ___ ___ ___| |___ ___ ___ "); Console.WriteLine($@"| | | -_| -_| . |_ -| | -_| -_| . |"); Console.WriteLine($@"|____/|___|___| _|___|_|___|___| _|"); Console.Write($@" |_| |_| "); Console.ForegroundColor = existingColor; Console.WriteLine($" v{deepSleepAssemblyInfo.version}"); Console.WriteLine($""); if (!string.IsNullOrWhiteSpace(deepSleepWebAssemblyInfo.framework)) { Console.WriteLine($""); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"Target(s): "); Console.ForegroundColor = existingColor; Console.WriteLine($"------------------------------------------------"); Console.ForegroundColor = existingColor; Console.WriteLine($" {deepSleepAssemblyInfo.name}, {deepSleepAssemblyInfo.version}, {deepSleepAssemblyInfo.framework}"); if (!string.IsNullOrWhiteSpace(deepSleepOpenApiAssemblyInfo.name)) { Console.WriteLine($" {deepSleepOpenApiAssemblyInfo.name}, {deepSleepOpenApiAssemblyInfo.version}, {deepSleepOpenApiAssemblyInfo.framework}"); } Console.WriteLine($" {deepSleepWebAssemblyInfo.name}, {deepSleepWebAssemblyInfo.version}, {deepSleepWebAssemblyInfo.framework}"); Console.WriteLine($""); } Console.ForegroundColor = ConsoleColor.Yellow; Console.Write($"Endpoints: "); Console.ForegroundColor = existingColor; Console.WriteLine($"{routingTable?.GetRoutes()?.Count ?? 0}"); Console.WriteLine($"------------------------------------------------"); Console.WriteLine($""); var routes = (routingTable?.GetRoutes() ?? new List <ApiRoutingItem>()) .OrderBy(r => r.Template) .ToList(); Action <string, string, bool> writeRoute = (string method, string template, bool isAutoHead) => { existingColor = Console.ForegroundColor; Console.ForegroundColor = isAutoHead ? ConsoleColor.Gray : ConsoleColor.Yellow; Console.Write($" {method.ToUpper(),-9}"); Console.ForegroundColor = existingColor; WriteEndpointTemplate(template, isAutoHead); }; routes.ForEach(r => { writeRoute(r.HttpMethod, r.Template, false); if (string.Compare(r.HttpMethod, "GET", true) == 0) { var enableGet = r.Configuration?.EnableHeadForGetRequests ?? defaultConfiguration?.EnableHeadForGetRequests ?? systemConfiguration?.EnableHeadForGetRequests ?? true; if (enableGet) { var matchingHead = routes .Where(m => m.HttpMethod.ToLowerInvariant() == "head") .Where(m => m.Template.ToLowerInvariant() == r.Template.ToLowerInvariant()) .FirstOrDefault(); if (matchingHead == null) { writeRoute("HEAD", r.Template, true); } } } }); Console.WriteLine(""); var now = DateTime.Now; if (now.Month == 5 && now.Day == 4) { MayTheFourth(); } Console.WriteLine(); }
/// <summary>Processes the API request.</summary> /// <param name="context">The context.</param> /// <param name="httpcontext">The httpcontext.</param> /// <param name="contextResolver">The context resolver.</param> /// <param name="requestPipeline">The request pipeline.</param> /// <param name="defaultRequestConfiguration">The default request configuration.</param> /// <returns></returns> internal static async Task <bool> ProcessApiRequest( this ApiRequestContext context, HttpContext httpcontext, IApiRequestContextResolver contextResolver, IApiRequestPipeline requestPipeline, IDeepSleepRequestConfiguration defaultRequestConfiguration) { if (!context.RequestAborted.IsCancellationRequested) { await requestPipeline.Run(contextResolver); context.SetThreadCulure(); var responseDate = DateTimeOffset.UtcNow; context.Response.Date = responseDate; context.Response.AddHeader( name: "Date", value: responseDate.ToString("r"), append: false, allowMultiple: false); httpcontext.Response.Headers.Add("Date", responseDate.ToString("r")); // Sync up the expire header for nocache requests with the date header being used var contextExpiresHeader = context.Response.Headers.FirstOrDefault(h => h.Name == "Expires"); var expirationSeconds = context.Configuration?.CacheDirective?.ExpirationSeconds ?? defaultRequestConfiguration?.CacheDirective?.ExpirationSeconds ?? ApiRequestContext.GetDefaultRequestConfiguration().CacheDirective.ExpirationSeconds.Value; var cacheability = context.Configuration?.CacheDirective?.Cacheability ?? defaultRequestConfiguration?.CacheDirective?.Cacheability ?? ApiRequestContext.GetDefaultRequestConfiguration().CacheDirective.Cacheability.Value; if (contextExpiresHeader != null) { if (cacheability == HttpCacheType.NoCache && expirationSeconds > 0) { contextExpiresHeader.Value = responseDate.AddSeconds(-1).ToString("r"); } else { contextExpiresHeader.Value = responseDate.AddSeconds(expirationSeconds).ToString("r"); } } // Merge status code to the http response httpcontext.Response.StatusCode = context.Response.StatusCode; if (context.Response.ResponseWriter != null && context.Response.ResponseWriterOptions != null) { context.Response.AddHeader( name: "Content-Type", value: context.Response.ContentType.ToString(), append: false, allowMultiple: false); if (!string.IsNullOrWhiteSpace(context.Response.ContentLanguage)) { context.Response.AddHeader( name: "Content-Language", value: context.Response.ContentLanguage, append: false, allowMultiple: false); } if (!context.Request.IsHeadRequest()) { var contentLength = await context.Response.ResponseWriter.WriteType( stream : httpcontext.Response.Body, obj : context.Response.ResponseObject, context.Response.ResponseWriterOptions, (l) => { context.Response.ContentLength = l; context.Response.AddHeader( name: "Content-Length", value: l.ToString(CultureInfo.InvariantCulture), append: false, allowMultiple: false); AddHeadersToResponse(httpcontext, context); }).ConfigureAwait(false); context.Response.ContentLength = contentLength; } else { using (var ms = new MemoryStream()) { await context.Response.ResponseWriter.WriteType( ms, context.Response.ResponseObject, context.Response.ResponseWriterOptions).ConfigureAwait(false); context.Response.ResponseObject = null; context.Response.ContentLength = ms.Length; context.Response.AddHeader( name: "Content-Length", value: ms.Length.ToString(CultureInfo.InvariantCulture), append: false, allowMultiple: false); AddHeadersToResponse(httpcontext, context); } } } else { context.Response.ContentLength = 0; context.Response.AddHeader("Content-Length", "0"); AddHeadersToResponse(httpcontext, context); } context.Runtime.Duration.UtcEnd = DateTime.UtcNow; } return(true); }
/// <summary>Merges the configurations.</summary> /// <param name="serviceProvider">The service provider.</param> /// <param name="defaultConfig">The default configuration.</param> /// <param name="endpointConfig">The endpoint configuration.</param> /// <returns></returns> public static IDeepSleepRequestConfiguration MergeConfigurations( IServiceProvider serviceProvider, IDeepSleepRequestConfiguration defaultConfig, IDeepSleepRequestConfiguration endpointConfig) { IDeepSleepRequestConfiguration systemConfig = ApiRequestContext.GetDefaultRequestConfiguration(); IDeepSleepRequestConfiguration requestConfig = (endpointConfig != null) ? GetNewConfiguration(serviceProvider, endpointConfig) : GetNewConfiguration(serviceProvider, defaultConfig); if (requestConfig == null) { return(systemConfig); } requestConfig.ApiErrorResponseProvider = endpointConfig?.ApiErrorResponseProvider ?? defaultConfig?.ApiErrorResponseProvider ?? systemConfig.ApiErrorResponseProvider; requestConfig.AllowAnonymous = endpointConfig?.AllowAnonymous ?? defaultConfig?.AllowAnonymous ?? systemConfig.AllowAnonymous; requestConfig.EnableHeadForGetRequests = endpointConfig?.EnableHeadForGetRequests ?? defaultConfig?.EnableHeadForGetRequests ?? systemConfig.EnableHeadForGetRequests; // ---------------------------- // Authorization Components // ---------------------------- if (endpointConfig?.AuthorizationProviders != null || defaultConfig?.AuthorizationProviders != null) { if (endpointConfig?.AuthorizationProviders != null) { requestConfig.AuthorizationProviders = new List <IAuthorizationComponent>(endpointConfig.AuthorizationProviders); } else if (defaultConfig?.AuthorizationProviders != null) { requestConfig.AuthorizationProviders = new List <IAuthorizationComponent>(defaultConfig.AuthorizationProviders); } else { requestConfig.AuthorizationProviders = systemConfig.AuthorizationProviders; } } else { requestConfig.AuthorizationProviders = systemConfig.AuthorizationProviders; } // ---------------------------- // Authentication Components // ---------------------------- if (endpointConfig?.AuthenticationProviders != null || defaultConfig?.AuthenticationProviders != null) { if (endpointConfig?.AuthenticationProviders != null) { requestConfig.AuthenticationProviders = new List <IAuthenticationComponent>(endpointConfig.AuthenticationProviders); } else if (defaultConfig?.AuthenticationProviders != null) { requestConfig.AuthenticationProviders = new List <IAuthenticationComponent>(defaultConfig.AuthenticationProviders); } else { requestConfig.AuthenticationProviders = systemConfig.AuthenticationProviders; } } else { requestConfig.AuthenticationProviders = systemConfig.AuthenticationProviders; } // ---------------------------- // Validator Components // ---------------------------- if (endpointConfig?.Validators != null || defaultConfig?.Validators != null) { if (endpointConfig?.Validators != null) { requestConfig.Validators = new List <IEndpointValidatorComponent>(endpointConfig.Validators); } else if (defaultConfig?.Validators != null) { requestConfig.Validators = new List <IEndpointValidatorComponent>(defaultConfig.Validators); } else { requestConfig.Validators = systemConfig.Validators; } } else { requestConfig.Validators = systemConfig.Validators; } // ---------------------------- // Pipeline Components // ---------------------------- if (endpointConfig?.PipelineComponents != null || defaultConfig?.PipelineComponents != null) { if (endpointConfig?.PipelineComponents != null) { requestConfig.PipelineComponents = new List <IRequestPipelineComponent>(endpointConfig.PipelineComponents); } else if (defaultConfig?.PipelineComponents != null) { requestConfig.PipelineComponents = new List <IRequestPipelineComponent>(defaultConfig.PipelineComponents); } else { requestConfig.PipelineComponents = systemConfig.PipelineComponents; } } else { requestConfig.PipelineComponents = systemConfig.PipelineComponents; } // ---------------------------- // Language Support Validation // ---------------------------- if (endpointConfig?.LanguageSupport != null || defaultConfig?.LanguageSupport != null) { requestConfig.LanguageSupport = new ApiLanguageSupportConfiguration { FallBackLanguage = endpointConfig?.LanguageSupport?.FallBackLanguage ?? defaultConfig?.LanguageSupport?.FallBackLanguage ?? systemConfig.LanguageSupport?.FallBackLanguage, SupportedLanguages = new List <string>(endpointConfig?.LanguageSupport?.SupportedLanguages ?? defaultConfig?.LanguageSupport?.SupportedLanguages ?? systemConfig.LanguageSupport?.SupportedLanguages), UseAcceptedLanguageAsThreadCulture = endpointConfig?.LanguageSupport?.UseAcceptedLanguageAsThreadCulture ?? defaultConfig?.LanguageSupport?.UseAcceptedLanguageAsThreadCulture ?? systemConfig.LanguageSupport?.UseAcceptedLanguageAsThreadCulture, UseAcceptedLanguageAsThreadUICulture = endpointConfig?.LanguageSupport?.UseAcceptedLanguageAsThreadUICulture ?? defaultConfig?.LanguageSupport?.UseAcceptedLanguageAsThreadUICulture ?? systemConfig.LanguageSupport?.UseAcceptedLanguageAsThreadUICulture }; var endpointSupportedLanguages = endpointConfig?.LanguageSupport?.SupportedLanguages != null ? new List <string>(endpointConfig.LanguageSupport.SupportedLanguages) : null; var defaultSupportedLanguages = defaultConfig?.LanguageSupport?.SupportedLanguages != null ? new List <string>(defaultConfig.LanguageSupport.SupportedLanguages) : null; requestConfig.LanguageSupport.SupportedLanguages = endpointSupportedLanguages ?? defaultSupportedLanguages ?? systemConfig.LanguageSupport?.SupportedLanguages; } else { requestConfig.LanguageSupport = systemConfig.LanguageSupport; } // ---------------------------- // Merge Request Validation // ---------------------------- if (endpointConfig?.RequestValidation != null || defaultConfig?.RequestValidation != null) { requestConfig.RequestValidation = new ApiRequestValidationConfiguration { MaxHeaderLength = endpointConfig?.RequestValidation?.MaxHeaderLength ?? defaultConfig?.RequestValidation?.MaxHeaderLength ?? systemConfig.RequestValidation?.MaxHeaderLength, MaxRequestUriLength = endpointConfig?.RequestValidation?.MaxRequestUriLength ?? defaultConfig?.RequestValidation?.MaxRequestUriLength ?? systemConfig.RequestValidation?.MaxRequestUriLength, MaxRequestLength = endpointConfig?.RequestValidation?.MaxRequestLength ?? defaultConfig?.RequestValidation?.MaxRequestLength ?? systemConfig.RequestValidation?.MaxRequestLength, AllowRequestBodyWhenNoModelDefined = endpointConfig?.RequestValidation?.AllowRequestBodyWhenNoModelDefined ?? defaultConfig?.RequestValidation?.AllowRequestBodyWhenNoModelDefined ?? systemConfig.RequestValidation?.AllowRequestBodyWhenNoModelDefined, RequireContentLengthOnRequestBodyRequests = endpointConfig?.RequestValidation?.RequireContentLengthOnRequestBodyRequests ?? defaultConfig?.RequestValidation?.RequireContentLengthOnRequestBodyRequests ?? systemConfig.RequestValidation?.RequireContentLengthOnRequestBodyRequests }; } else { requestConfig.RequestValidation = systemConfig.RequestValidation; } // ---------------------------- // Merge Cache Directive // ---------------------------- if (endpointConfig?.CacheDirective != null || defaultConfig?.CacheDirective != null) { requestConfig.CacheDirective = new ApiCacheDirectiveConfiguration { Cacheability = endpointConfig?.CacheDirective?.Cacheability ?? defaultConfig?.CacheDirective?.Cacheability ?? systemConfig.CacheDirective?.Cacheability, CacheLocation = endpointConfig?.CacheDirective?.CacheLocation ?? defaultConfig?.CacheDirective?.CacheLocation ?? systemConfig.CacheDirective?.CacheLocation, ExpirationSeconds = endpointConfig?.CacheDirective?.ExpirationSeconds ?? defaultConfig?.CacheDirective?.ExpirationSeconds ?? systemConfig.CacheDirective?.ExpirationSeconds, VaryHeaderValue = endpointConfig?.CacheDirective?.VaryHeaderValue ?? defaultConfig?.CacheDirective?.VaryHeaderValue ?? systemConfig.CacheDirective?.VaryHeaderValue }; } else { requestConfig.CacheDirective = systemConfig.CacheDirective; } // ---------------------------- // Merge Cross Origin Configuration // ---------------------------- if (endpointConfig?.CrossOriginConfig != null || defaultConfig?.CrossOriginConfig != null) { requestConfig.CrossOriginConfig = new ApiCrossOriginConfiguration { AllowCredentials = endpointConfig?.CrossOriginConfig?.AllowCredentials ?? defaultConfig?.CrossOriginConfig?.AllowCredentials ?? systemConfig?.CrossOriginConfig?.AllowCredentials, MaxAgeSeconds = endpointConfig?.CrossOriginConfig?.MaxAgeSeconds ?? defaultConfig?.CrossOriginConfig?.MaxAgeSeconds ?? systemConfig?.CrossOriginConfig?.MaxAgeSeconds }; var endpointAllowedOrigins = endpointConfig?.CrossOriginConfig?.AllowedOrigins != null ? new List <string>(endpointConfig.CrossOriginConfig.AllowedOrigins) : null; var defaultAllowedOrigins = defaultConfig?.CrossOriginConfig?.AllowedOrigins != null ? new List <string>(defaultConfig.CrossOriginConfig.AllowedOrigins) : null; requestConfig.CrossOriginConfig.AllowedOrigins = endpointAllowedOrigins ?? defaultAllowedOrigins ?? systemConfig.CrossOriginConfig?.AllowedOrigins; var endpointExposeHeaders = endpointConfig?.CrossOriginConfig?.ExposeHeaders != null ? new List <string>(endpointConfig.CrossOriginConfig.ExposeHeaders) : null; var defaultExposeHeaders = defaultConfig?.CrossOriginConfig?.ExposeHeaders != null ? new List <string>(defaultConfig.CrossOriginConfig.ExposeHeaders) : null; requestConfig.CrossOriginConfig.ExposeHeaders = endpointExposeHeaders ?? defaultExposeHeaders ?? systemConfig.CrossOriginConfig?.ExposeHeaders; var endpointAllowedHeaders = endpointConfig?.CrossOriginConfig?.AllowedHeaders != null ? new List <string>(endpointConfig.CrossOriginConfig.AllowedHeaders) : null; var defaultAllowedHeaders = defaultConfig?.CrossOriginConfig?.AllowedHeaders != null ? new List <string>(defaultConfig.CrossOriginConfig.AllowedHeaders) : null; requestConfig.CrossOriginConfig.AllowedHeaders = endpointAllowedHeaders ?? defaultAllowedHeaders ?? systemConfig.CrossOriginConfig?.AllowedHeaders; } else { requestConfig.CrossOriginConfig = systemConfig.CrossOriginConfig; } // ---------------------------- // Merge Read Write Configuration // ---------------------------- if (endpointConfig?.ReadWriteConfiguration != null || defaultConfig?.ReadWriteConfiguration != null) { requestConfig.ReadWriteConfiguration = new ApiMediaSerializerConfiguration { ReaderResolver = endpointConfig?.ReadWriteConfiguration?.ReaderResolver ?? defaultConfig?.ReadWriteConfiguration?.ReaderResolver ?? systemConfig.ReadWriteConfiguration?.ReaderResolver, WriterResolver = endpointConfig?.ReadWriteConfiguration?.WriterResolver ?? defaultConfig?.ReadWriteConfiguration?.WriterResolver ?? systemConfig.ReadWriteConfiguration?.WriterResolver, AcceptHeaderOverride = endpointConfig?.ReadWriteConfiguration?.AcceptHeaderOverride ?? defaultConfig?.ReadWriteConfiguration?.AcceptHeaderOverride ?? systemConfig.ReadWriteConfiguration?.AcceptHeaderOverride, }; var endpointReadableMediaTypes = endpointConfig?.ReadWriteConfiguration?.ReadableMediaTypes != null ? new List <string>(endpointConfig.ReadWriteConfiguration.ReadableMediaTypes) : null; var defaultReadableMediaTypes = defaultConfig?.ReadWriteConfiguration?.ReadableMediaTypes != null ? new List <string>(defaultConfig.ReadWriteConfiguration.ReadableMediaTypes) : null; var endpointWriteableMediaTypes = endpointConfig?.ReadWriteConfiguration?.WriteableMediaTypes != null ? new List <string>(endpointConfig.ReadWriteConfiguration.WriteableMediaTypes) : null; var defaultWriteableMediaTypes = defaultConfig?.ReadWriteConfiguration?.WriteableMediaTypes != null ? new List <string>(defaultConfig.ReadWriteConfiguration.WriteableMediaTypes) : null; requestConfig.ReadWriteConfiguration.ReadableMediaTypes = endpointReadableMediaTypes ?? defaultReadableMediaTypes ?? systemConfig.ReadWriteConfiguration?.ReadableMediaTypes; requestConfig.ReadWriteConfiguration.WriteableMediaTypes = endpointWriteableMediaTypes ?? defaultWriteableMediaTypes ?? systemConfig.ReadWriteConfiguration?.WriteableMediaTypes; } else { requestConfig.ReadWriteConfiguration = systemConfig.ReadWriteConfiguration; } // ------------------------------------ // Merge Validation Error Configuration // ------------------------------------ if (endpointConfig?.ValidationErrorConfiguration != null || defaultConfig?.ValidationErrorConfiguration != null) { requestConfig.ValidationErrorConfiguration = new ApiValidationErrorConfiguration { UriBindingError = endpointConfig?.ValidationErrorConfiguration?.UriBindingError ?? defaultConfig?.ValidationErrorConfiguration?.UriBindingError ?? systemConfig.ValidationErrorConfiguration?.UriBindingError, UriBindingValueError = endpointConfig?.ValidationErrorConfiguration?.UriBindingValueError ?? defaultConfig?.ValidationErrorConfiguration?.UriBindingValueError ?? systemConfig.ValidationErrorConfiguration?.UriBindingValueError, RequestDeserializationError = endpointConfig?.ValidationErrorConfiguration?.RequestDeserializationError ?? defaultConfig?.ValidationErrorConfiguration?.RequestDeserializationError ?? systemConfig.ValidationErrorConfiguration?.RequestDeserializationError, HttpStatusMode = endpointConfig?.ValidationErrorConfiguration?.HttpStatusMode ?? defaultConfig?.ValidationErrorConfiguration?.HttpStatusMode ?? systemConfig.ValidationErrorConfiguration.HttpStatusMode }; } else { requestConfig.ValidationErrorConfiguration = systemConfig.ValidationErrorConfiguration; } return(requestConfig); }
/// <summary>Gets the route information.</summary> /// <param name="context">The context.</param> /// <param name="resolver">The resolver.</param> /// <param name="routes">The routes.</param> /// <param name="defaultRequestConfiguration">The default request configuration.</param> /// <returns></returns> private static async Task <ApiRoutingItem> GetRoutingItem(this ApiRequestContext context, IUriRouteResolver resolver, IApiRoutingTable routes, IDeepSleepRequestConfiguration defaultRequestConfiguration) { // ----------------------------------------------------------------- // We want to trick the routing engine to treat HEAD requests as GET // http://tools.ietf.org/html/rfc7231#section-4.3.2 // ----------------------------------------------------------------- ApiRoutingItem routeInfo; if (context.Request.Method.In(StringComparison.InvariantCultureIgnoreCase, "HEAD")) { routeInfo = await resolver.MatchRoute( routes, "HEAD", context.Request.Path).ConfigureAwait(false); if (routeInfo == null) { routeInfo = await resolver.MatchRoute( routes, "GET", context.Request.Path).ConfigureAwait(false); if (routeInfo != null) { var enableHeadForGetRequests = routeInfo.Configuration?.EnableHeadForGetRequests ?? defaultRequestConfiguration?.EnableHeadForGetRequests ?? ApiRequestContext.GetDefaultRequestConfiguration().EnableHeadForGetRequests ?? true; if (!enableHeadForGetRequests) { routeInfo = null; } } } } else if (context.Request.IsCorsPreflightRequest()) { if (context.Request.CrossOriginRequest.AccessControlRequestMethod.In(StringComparison.InvariantCultureIgnoreCase, "HEAD")) { routeInfo = await resolver.MatchRoute( routes, context.Request.CrossOriginRequest.AccessControlRequestMethod, context.Request.Path).ConfigureAwait(false); if (routeInfo == null) { routeInfo = await resolver.MatchRoute( routes, "GET", context.Request.Path).ConfigureAwait(false); if (routeInfo != null) { var enableHeadForGetRequests = routeInfo.Configuration?.EnableHeadForGetRequests ?? defaultRequestConfiguration?.EnableHeadForGetRequests ?? ApiRequestContext.GetDefaultRequestConfiguration().EnableHeadForGetRequests ?? true; if (!enableHeadForGetRequests) { routeInfo = null; } } } } else { routeInfo = await resolver.MatchRoute( routes, context.Request.CrossOriginRequest.AccessControlRequestMethod, context.Request.Path).ConfigureAwait(false); } } else { routeInfo = await resolver.MatchRoute( routes, context.Request.Method, context.Request.Path).ConfigureAwait(false); } return(routeInfo); }
/// <summary>Processes the HTTP request cross origin resource sharing preflight.</summary> /// <param name="context">The context.</param> /// <param name="routes">The routes.</param> /// <param name="resolver">The resolver.</param> /// <param name="defaultRequestConfiguration">The default request configuration.</param> /// <returns></returns> internal static async Task <bool> ProcessHttpRequestCrossOriginResourceSharingPreflight( this ApiRequestContext context, IApiRoutingTable routes, IUriRouteResolver resolver, IDeepSleepRequestConfiguration defaultRequestConfiguration) { if (!context.RequestAborted.IsCancellationRequested) { if (context.Request?.IsCorsPreflightRequest() ?? false) { var methods = (context.Routing?.Template?.Locations ?? new List <ApiEndpointLocation>()) .Where(r => !string.IsNullOrWhiteSpace(r.HttpMethod)) .Select(r => r.HttpMethod.ToUpper()) .Distinct() .ToList(); // Need to include the auto-enabled HEAD method if configured on the get endpoint (if available) var hasGet = methods.FirstOrDefault(m => m.Equals("GET", StringComparison.InvariantCultureIgnoreCase)) != null; if (hasGet) { var hasHead = methods.FirstOrDefault(m => m.Equals("HEAD", StringComparison.InvariantCultureIgnoreCase)) != null; if (!hasHead) { if (resolver != null) { var getMatch = await resolver.MatchRoute( routes, "GET", context.Request.Path).ConfigureAwait(false); if (getMatch != null) { var enableHeadForGetRequests = getMatch.Configuration?.EnableHeadForGetRequests ?? defaultRequestConfiguration?.EnableHeadForGetRequests ?? ApiRequestContext.GetDefaultRequestConfiguration().EnableHeadForGetRequests ?? true; if (enableHeadForGetRequests) { methods.Add("HEAD"); } } } } } context.Response.StatusCode = 200; context.Response.AddHeader("Access-Control-Allow-Methods", string.Join(", ", methods).Trim()); if (!string.IsNullOrWhiteSpace(context.Request?.CrossOriginRequest?.AccessControlRequestHeaders)) { var allowHeaders = (context.Configuration?.CrossOriginConfig?.AllowedHeaders ?? new string[] { }) .Distinct() .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(i => i.Trim()) .ToList(); if (allowHeaders.Count > 0 && allowHeaders.Contains("*")) { context.Response.AddHeader("Access-Control-Allow-Headers", context.Request.CrossOriginRequest.AccessControlRequestHeaders); } else { context.Response.AddHeader("Access-Control-Allow-Headers", string.Join(", ", allowHeaders)); } } if (context.Configuration?.CrossOriginConfig?.MaxAgeSeconds.HasValue ?? false) { context.Response.AddHeader("Access-Control-Max-Age", $"{context.Configuration.CrossOriginConfig.MaxAgeSeconds.Value}"); } return(false); } return(true); } return(false); }