/// <summary>Resolves the configuration.</summary> /// <param name="serviceProvider">The service provider.</param> /// <param name="config">The configuration.</param> /// <returns></returns> private static IDeepSleepRequestConfiguration GetNewConfiguration(IServiceProvider serviceProvider, IDeepSleepRequestConfiguration config) { if (config == null) { return(null); } IDeepSleepRequestConfiguration init = null; if (serviceProvider != null) { try { init = serviceProvider.GetService(config.GetType()) as IDeepSleepRequestConfiguration; } catch { } } if (init == null) { init = Activator.CreateInstance(config.GetType()) as IDeepSleepRequestConfiguration; } return(init); }
/// <summary>Initializes a new instance of the <see cref="DeepSleepRouteRegistration"/> class.</summary> /// <param name="template">The template.</param> /// <param name="httpMethods">The HTTP methods.</param> /// <param name="controller">The controller.</param> /// <param name="endpoint">The endpoint.</param> /// <param name="config">The configuration.</param> /// <exception cref="System.Exception"> /// Controller must be specified /// or /// Endpoint must be specified /// or /// </exception> /// <exception cref="System.MissingMethodException"></exception> public DeepSleepRouteRegistration( string template, IList <string> httpMethods, Type controller, string endpoint, IDeepSleepRequestConfiguration config) : this(template, httpMethods, controller, endpoint, null, config) { }
/// <summary>Initializes a new instance of the <see cref="DeepSleepRouteRegistration" /> class.</summary> /// <param name="template">The template.</param> /// <param name="httpMethods">The HTTP methods.</param> /// <param name="controller">The controller.</param> /// <param name="methodInfo">The method information.</param> /// <param name="config">The configuration.</param> /// <exception cref="Exception">Controller must be specified /// or /// Endpoint must be specified /// or</exception> public DeepSleepRouteRegistration( string template, IList <string> httpMethods, Type controller, MethodInfo methodInfo, IDeepSleepRequestConfiguration config) : this(template, httpMethods, controller, methodInfo?.Name, methodInfo, config) { }
/// <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); }
private DeepSleepRouteRegistration( string template, IList <string> httpMethods, Type controller, string endpoint, MethodInfo methodInfo, IDeepSleepRequestConfiguration config) { if (controller == null) { throw new Exception("Controller must be specified"); } if (string.IsNullOrWhiteSpace(endpoint) && methodInfo == null) { throw new Exception($"{nameof(endpoint)} or {nameof(methodInfo)} must be specified"); } if (httpMethods?.Count == 0) { throw new Exception($"Http methods not specified on endpoint {endpoint} in controller {controller.FullName}"); } this.Template = template ?? string.Empty; this.HttpMethods = httpMethods.Select(m => m.ToUpperInvariant()).ToList(); this.Configuration = config; this.Controller = Type.GetType(controller.AssemblyQualifiedName); this.Endpoint = endpoint; if (methodInfo != null) { var parameters = methodInfo.GetParameters(); var methods = this.Controller .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod) .Where(m => m.Name == methodInfo.Name) .ToList(); if (methods.Count == 0) { this.MethodInfo = null; } else if (methods.Count == 1) { this.MethodInfo = methods[0]; } else { // TODO: Handle this for overloads } } this.MethodInfo = methodInfo; }
internal static async Task <IList <string> > GetRequestBodyContentTypes( Type requestBodyType, IServiceProvider serviceProvider, IDeepSleepRequestConfiguration routeConfiguration, IDeepSleepMediaSerializerFactory formatterFactory, OpenApiSpecVersion specVersion = OpenApiSpecVersion.OpenApi3_0) { var formatterTypes = routeConfiguration?.ReadWriteConfiguration?.ReadableMediaTypes ?? formatterFactory?.GetReadableTypes(objType: requestBodyType, overridingFormatters: null) ?? new List <string>(); if (routeConfiguration?.ReadWriteConfiguration?.ReaderResolver != null) { var overrides = await routeConfiguration.ReadWriteConfiguration.ReaderResolver(serviceProvider).ConfigureAwait(false); if (overrides?.Formatters != null) { formatterTypes = overrides.Formatters .Where(f => f != null) .Where(f => f.CanHandleType(requestBodyType)) .Where(f => f.SupportsRead) .Where(f => f.ReadableMediaTypes != null) .SelectMany(f => f.ReadableMediaTypes) .Distinct() .ToList(); formatterTypes = routeConfiguration?.ReadWriteConfiguration?.ReadableMediaTypes ?? formatterTypes ?? new List <string>(); } else { formatterTypes = routeConfiguration?.ReadWriteConfiguration?.ReadableMediaTypes ?? new List <string>(); } } if (specVersion == OpenApiSpecVersion.OpenApi2_0) { formatterTypes = formatterTypes .Where(type => string.Compare(type, "application/x-www-form-urlencoded", true) != 0) .ToList(); } return(formatterTypes.ToList()); }
internal static async Task <IList <string> > GetResponseBodyContentTypes( Type responseBodyType, IServiceProvider serviceProvider, IDeepSleepRequestConfiguration routeConfiguration, IDeepSleepMediaSerializerFactory formatterFactory) { var formatterTypes = routeConfiguration?.ReadWriteConfiguration?.WriteableMediaTypes ?? formatterFactory?.GetWriteableTypes(objType: responseBodyType, overridingFormatters: null) ?? new List <string>(); if (routeConfiguration?.ReadWriteConfiguration?.WriterResolver != null) { var overrides = await routeConfiguration.ReadWriteConfiguration.WriterResolver(serviceProvider).ConfigureAwait(false); if (overrides?.Formatters != null) { formatterTypes = routeConfiguration?.ReadWriteConfiguration?.WriteableMediaTypes ?? formatterFactory?.GetWriteableTypes(objType: responseBodyType, overridingFormatters: overrides.Formatters) ?? new List <string>(); } } return(formatterTypes?.ToList() ?? new List <string>()); }
/// <summary>Adds the route.</summary> /// <param name="template">The template.</param> /// <param name="httpMethods">The HTTP methods.</param> /// <param name="controller">The controller.</param> /// <param name="endpoint">The endpoint.</param> /// <param name="config">The configuration.</param> /// <returns></returns> public StaticRouteDiscoveryStrategy AddRoute(string template, IList <string> httpMethods, Type controller, string endpoint, IDeepSleepRequestConfiguration config) { var registration = new DeepSleepRouteRegistration( template: template, httpMethods: httpMethods, controller: controller, endpoint: endpoint, config: config); registrations.Add(registration); return(this); }
/// <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>Processes the HTTP request routing.</summary> /// <param name="context">The context.</param> /// <param name="routes">The routes.</param> /// <param name="resolver">The resolver.</param> /// <param name="defaultRequestConfig">The default request configuration.</param> /// <returns></returns> internal static async Task <bool> ProcessHttpRequestRouting(this ApiRequestContext context, IApiRoutingTable routes, IUriRouteResolver resolver, IDeepSleepRequestConfiguration defaultRequestConfig) { if (!context.RequestAborted.IsCancellationRequested) { if (routes != null && resolver != null) { context.Routing.Route = await context.GetRoutingItem(resolver, routes, defaultRequestConfig).ConfigureAwait(false); context.Routing.Template = await context.GetRoutingTemplate(resolver, routes).ConfigureAwait(false); } context.Configuration = MergeConfigurations( serviceProvider: context?.RequestServices, defaultConfig: defaultRequestConfig, endpointConfig: context.Routing?.Route?.Configuration); if (context.Routing.Route?.Location?.MethodInfo != null) { var attributes = context.Routing.Route.Location.MethodInfo.GetCustomAttributes(); // Find any attribute request pipeline components // and add the to the final configuration of the request. attributes .Where(a => a as IRequestPipelineComponent != null) .Select(a => a as IRequestPipelineComponent) .ToList() .ForEach(p => context.Configuration.PipelineComponents.Add(p)); // Find any attribute validator components // and add the to the final configuration of the request. attributes .Where(a => a as IEndpointValidatorComponent != null) .Select(a => a as IEndpointValidatorComponent) .ToList() .ForEach(v => context.Configuration.Validators.Add(v)); // Find any authentication components // and add the to the final configuration of the request. var authenticationComponents = attributes .Where(a => a as IAuthenticationComponent != null) .Select(a => a as IAuthenticationComponent) .ToList(); if (authenticationComponents.Count > 0) { context.Configuration.AuthenticationProviders = authenticationComponents; } // Find any authorization components // and add the to the final configuration of the request. var authorizationComponents = attributes .Where(a => a as IAuthorizationComponent != null) .Select(a => a as IAuthorizationComponent) .ToList(); if (authorizationComponents.Count > 0) { context.Configuration.AuthorizationProviders = authorizationComponents; } } if (context.Configuration?.RequestValidation?.MaxRequestLength != null && context.ConfigureMaxRequestLength != null) { try { context.ConfigureMaxRequestLength(context.Configuration.RequestValidation.MaxRequestLength.Value); } catch { } } return(true); } return(false); }
/// <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); }