private IHttpResponseHandler GetHttpResponseHandler(HttpFunctionDefinition httpFunctionDefinition) { IHttpResponseHandler httpResponseHandler = httpFunctionDefinition.HasHttpResponseHandler ? (IHttpResponseHandler)ServiceProvider.GetRequiredService(httpFunctionDefinition.HttpResponseHandlerType) : new DefaultHttpResponseHandler(); return(httpResponseHandler); }
public SignalRFunctionConfigurationBuilder( ConnectionStringSettingNames connectionStringSettingNames, ISignalRFunctionBuilder httpFunctionBuilder, HttpFunctionDefinition definition) { _connectionStringSettingNames = connectionStringSettingNames; _httpFunctionBuilder = httpFunctionBuilder; _definition = definition; }
private static void CompleteHttpFunctionDefinition(FunctionHostBuilder builder, HttpFunctionDefinition httpFunctionDefinition, AuthorizationBuilder authorizationBuilder, Type validationResultType) { if (!httpFunctionDefinition.Authorization.HasValue) { httpFunctionDefinition.Authorization = authorizationBuilder.AuthorizationDefaultValue; } if (httpFunctionDefinition.Authorization.Value == AuthorizationTypeEnum.TokenValidation) { httpFunctionDefinition.ValidatesToken = true; } if (httpFunctionDefinition.Verbs.Count == 0) { httpFunctionDefinition.Verbs.Add(HttpMethod.Get); } httpFunctionDefinition.ClaimsPrincipalAuthorizationType = httpFunctionDefinition.ClaimsPrincipalAuthorizationType ?? httpFunctionDefinition.RouteConfiguration.ClaimsPrincipalAuthorizationType ?? authorizationBuilder.DefaultClaimsPrincipalAuthorizationType; httpFunctionDefinition.HeaderBindingConfiguration = httpFunctionDefinition.HeaderBindingConfiguration ?? builder.DefaultHeaderBindingConfiguration; httpFunctionDefinition.HttpResponseHandlerType = httpFunctionDefinition.HttpResponseHandlerType ?? builder.DefaultHttpResponseHandlerType; httpFunctionDefinition.TokenHeader = httpFunctionDefinition.TokenHeader ?? authorizationBuilder.AuthorizationHeader ?? "Authorization"; httpFunctionDefinition.IsValidationResult = httpFunctionDefinition.CommandResultType != null && validationResultType.IsAssignableFrom(httpFunctionDefinition .CommandResultType); httpFunctionDefinition.IsStreamCommand = (typeof(IStreamCommand)).IsAssignableFrom(httpFunctionDefinition.CommandType); httpFunctionDefinition.TokenValidatorType = httpFunctionDefinition.TokenValidatorType ?? authorizationBuilder.TokenValidatorType; if (httpFunctionDefinition.ValidatesToken && httpFunctionDefinition.TokenValidatorType == null) { throw new ConfigurationException($"Command {httpFunctionDefinition.CommandType.Name} expects to be authenticated with token validation but no token validator is registered"); } httpFunctionDefinition.Route = httpFunctionDefinition.Route?.TrimStart('/'); ExtractRouteParameters(httpFunctionDefinition); ExtractPossibleQueryParameters(httpFunctionDefinition); ExtractPossibleFormParameters(httpFunctionDefinition); EnsureOpenApiDescription(httpFunctionDefinition); }
private IActionResult CreateResponse(int code, object content, HttpFunctionDefinition httpFunctionDefinition) { ISerializer serializer = CreateSerializer(httpFunctionDefinition); ContentResult result = new ContentResult { Content = serializer.Serialize(content), ContentType = "application/json", StatusCode = code }; return(result); }
/// <summary> /// Runs a command through the IActionResult ASP.Net pathways and returns a HTTP response. /// This is useful for testing end to end HTTP triggered functions without having to actually host the /// function app. /// A method only needs specifying in the function supports multiple methods. /// </summary> public async Task <HttpResponse> ExecuteHttpAsync <TResult>(ICommand <TResult> command, HttpMethod method = null) { HttpFunctionDefinition httpFunctionDefinition = FindHttpFunctionDefinition(command); ActionContext actionContext = _aspNetRuntime.PrepareToExecuteHttp(command, httpFunctionDefinition, method); IHttpResponseHandler httpResponseHandler = GetHttpResponseHandler(httpFunctionDefinition); HttpDispatcher httpDispatcher = new HttpDispatcher(Dispatcher, ServiceProvider); IActionResult actionResult = await httpDispatcher.DispatchAndConvertToActionResult(command, httpResponseHandler, httpFunctionDefinition); return(await _aspNetRuntime.CreateHttpResponse(actionContext, actionResult)); }
private ISerializer CreateSerializer(HttpFunctionDefinition httpFunctionDefinition) { if (httpFunctionDefinition.SerializerNamingStrategyType != null) { NamingStrategy serializerNamingStrategy = (NamingStrategy)Activator.CreateInstance(httpFunctionDefinition.SerializerNamingStrategyType); NamingStrategy deserializerNamingStrategy = (NamingStrategy)Activator.CreateInstance(httpFunctionDefinition.DeserializerNamingStrategyType); ISerializer serializer = new NamingStrategyJsonSerializer(deserializerNamingStrategy, serializerNamingStrategy); return(serializer); } return((ISerializer)_serviceProvider.GetRequiredService(httpFunctionDefinition.CommandDeserializerType)); }
private IHttpFunctionBuilderMetadataBuilder BuildHttpFunction <TCommand>(string route, AuthorizationTypeEnum?authorizationType, params HttpMethod[] method) where TCommand : ICommand { HttpFunctionDefinition definition = new HttpFunctionDefinition(typeof(TCommand)) { SubRoute = route, RouteConfiguration = _routeConfiguration, Route = string.Concat(_routeConfiguration.Route, route), Verbs = new HashSet <HttpMethod>(method), Authorization = authorizationType }; _definitions.Add(definition); return(new HttpFunctionBuilderMetadataBuilder(this, definition)); }
private static void ExtractPossibleFormParameters(HttpFunctionDefinition httpFunctionDefinition) { httpFunctionDefinition.PossibleFormProperties = httpFunctionDefinition .CommandType .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.GetCustomAttribute <SecurityPropertyAttribute>() == null && x.SetMethod != null && (x.PropertyType == typeof(IFormCollection))) .Select(x => new HttpParameter { Name = x.Name, Type = x.PropertyType }) .ToArray(); }
private IHttpFunctionConfigurationBuilder <TCommand> BuildHttpFunction <TCommand>(string route, AuthorizationTypeEnum?authorizationType, params HttpMethod[] method) { HttpFunctionDefinition definition = new HttpFunctionDefinition(typeof(TCommand)) { SubRoute = route, RouteConfiguration = _routeConfiguration, Route = _routeConfiguration.Route == null ? route : string.Concat(_routeConfiguration.Route, route).TrimStart('/'), Verbs = new HashSet <HttpMethod>(method), Authorization = authorizationType, ClaimsPrincipalAuthorizationType = _routeConfiguration.ClaimsPrincipalAuthorizationType }; _definitions.Add(definition); return(new HttpFunctionConfigurationBuilder <TCommand>(_connectionStringSettingNames, this, definition)); }
private static void ExtractPossibleQueryParameters(HttpFunctionDefinition httpFunctionDefinition) { httpFunctionDefinition.PossibleBindingProperties = httpFunctionDefinition .CommandType .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.GetCustomAttribute <SecurityPropertyAttribute>() == null && x.SetMethod != null && (x.PropertyType == typeof(string) || x.PropertyType .GetMethods(BindingFlags.Public | BindingFlags.Static).Any(y => y.Name == "TryParse"))) .Select(x => new HttpParameter { Name = x.Name, TypeName = x.PropertyType.EvaluateType(), Type = x.PropertyType }) .ToArray(); }
public ActionContext PrepareToExecuteHttp(ICommand command, HttpFunctionDefinition httpFunctionDefinition, HttpMethod method) { HttpContext httpContext = new DefaultHttpContext() { RequestServices = ServiceProvider, Request = { Method = (method ?? httpFunctionDefinition.Verbs.Single()).Method }, Response = { Body = new MemoryStream() } }; RouteData routeData = new RouteData(); ActionDescriptor actionDescriptor = new ActionDescriptor(); return(new ActionContext(httpContext, routeData, actionDescriptor)); }
public async Task <IActionResult> DispatchAndConvertToActionResult <TResult>( ICommand <TResult> command, IHttpResponseHandler httpResponseHandler, HttpFunctionDefinition httpFunctionDefinition) { IActionResult actionResult = null; try { TResult result = await _dispatcher.DispatchAsync(command); // TODO: Handle validation here Task <IActionResult> responseHandlerTask = httpResponseHandler.CreateResponse(command, result); if (responseHandlerTask != null) { actionResult = await responseHandlerTask; } if (actionResult == null) { actionResult = CreateResponse(200, result, httpFunctionDefinition); } } catch (ValidationException vex) { actionResult = await CreateValidationResponse(command, vex.ValidationResult, httpFunctionDefinition, httpResponseHandler); } catch (Exception ex) { Task exceptionResponseHandlerTask = httpResponseHandler.CreateResponseFromException(command, ex); if (exceptionResponseHandlerTask != null) { actionResult = await httpResponseHandler.CreateResponseFromException(command, ex); } if (actionResult == null) { actionResult = CreateResponse(500, "Unexpected error", httpFunctionDefinition); } } return(actionResult); }
private static void CompleteHttpFunctionDefinition(FunctionHostBuilder builder, HttpFunctionDefinition httpFunctionDefinition, AuthorizationBuilder authorizationBuilder, Type validationResultType) { if (!httpFunctionDefinition.Authorization.HasValue) { httpFunctionDefinition.Authorization = authorizationBuilder.AuthorizationDefaultValue; } if (httpFunctionDefinition.Authorization.Value == AuthorizationTypeEnum.TokenValidation) { httpFunctionDefinition.ValidatesToken = true; } if (httpFunctionDefinition.Verbs.Count == 0) { httpFunctionDefinition.Verbs.Add(HttpMethod.Get); } if (string.IsNullOrWhiteSpace(httpFunctionDefinition.ClaimsPrincipalAuthorizationTypeName)) { httpFunctionDefinition.ClaimsPrincipalAuthorizationType = authorizationBuilder.DefaultClaimsPrincipalAuthorizationType; } httpFunctionDefinition.HeaderBindingConfiguration = httpFunctionDefinition.HeaderBindingConfiguration ?? builder.DefaultHeaderBindingConfiguration; httpFunctionDefinition.HttpResponseHandlerType = httpFunctionDefinition.HttpResponseHandlerType ?? builder.DefaultHttpResponseHandlerType; httpFunctionDefinition.TokenHeader = authorizationBuilder.AuthorizationHeader ?? "Authorization"; httpFunctionDefinition.IsValidationResult = httpFunctionDefinition.CommandResultType != null && validationResultType.IsAssignableFrom(httpFunctionDefinition .CommandResultType); ExtractPossibleQueryParameters(httpFunctionDefinition); ExtractRouteParameters(httpFunctionDefinition); EnsureOpenApiDescription(httpFunctionDefinition); }
private static void ExtractPossibleQueryParameters(HttpFunctionDefinition httpFunctionDefinition) { Debug.Assert(httpFunctionDefinition.RouteParameters != null); httpFunctionDefinition.PossibleBindingProperties = httpFunctionDefinition .CommandType .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.GetCustomAttribute <SecurityPropertyAttribute>() == null && x.SetMethod != null && x.PropertyType.IsSupportedQueryParameterType() && httpFunctionDefinition.RouteParameters.All(y => y.Name != x.Name) // we can't be a query parameter and a route parameter ) .Select(x => new HttpParameter { Name = x.Name, Type = x.PropertyType, IsOptional = !x.PropertyType.IsValueType || Nullable.GetUnderlyingType(x.PropertyType) != null }) .ToArray(); }
private static void ExtractRouteParameters(HttpFunctionDefinition httpFunctionDefinition1) { string lowerCaseRoute = httpFunctionDefinition1.Route.ToLower(); httpFunctionDefinition1.RouteParameters = httpFunctionDefinition1 .CommandType .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.GetCustomAttribute <SecurityPropertyAttribute>() == null && x.SetMethod != null && (x.PropertyType == typeof(string) || x.PropertyType .GetMethods(BindingFlags.Public | BindingFlags.Static).Any(y => y.Name == "TryParse")) && lowerCaseRoute.Contains("{" + x.Name.ToLower() + "}")) .Select(x => new HttpParameter { Name = x.Name, TypeName = x.PropertyType.EvaluateType(), Type = x.PropertyType }) .ToArray(); }
private static void EnsureOpenApiDescription(HttpFunctionDefinition httpFunctionDefinition) { // function definitions share route definitions so setting properties for one sets for all // but we set only if absent and based on the parent route // alternative would be to gather up the unique route configurations and set once but will // involve multiple loops Debug.Assert(httpFunctionDefinition.RouteConfiguration != null); if (string.IsNullOrWhiteSpace(httpFunctionDefinition.RouteConfiguration.OpenApiName)) { string[] components = httpFunctionDefinition.RouteConfiguration.Route.Split('/'); for (int index = components.Length - 1; index >= 0; index--) { if (string.IsNullOrWhiteSpace(components[index]) || IsRouteParameter(components[index])) { continue; } httpFunctionDefinition.RouteConfiguration.OpenApiName = components[index]; break; } } }
private static void PatchHeaderBindings(FunctionHostBuilder builder, HttpFunctionDefinition httpFunctionDefinition) { if (httpFunctionDefinition.HeaderBindingConfiguration == null) { httpFunctionDefinition.HeaderBindingConfiguration = builder.DefaultHeaderBindingConfiguration; } else { if (builder.DefaultHeaderBindingConfiguration != null) { foreach (KeyValuePair <string, string> kvp in builder.DefaultHeaderBindingConfiguration .PropertyFromHeaderMappings) { if (!httpFunctionDefinition.HeaderBindingConfiguration.PropertyFromHeaderMappings.ContainsKey( kvp.Key)) { httpFunctionDefinition.HeaderBindingConfiguration.PropertyFromHeaderMappings .Add(kvp.Key, kvp.Value); } } } } if (httpFunctionDefinition.HeaderBindingConfiguration == null) { httpFunctionDefinition.HeaderBindingConfiguration = new HeaderBindingConfiguration() { PropertyFromHeaderMappings = new Dictionary <string, string>() }; } if (httpFunctionDefinition.HeaderBindingConfiguration.PropertyFromHeaderMappings == null) { httpFunctionDefinition.HeaderBindingConfiguration.PropertyFromHeaderMappings = new Dictionary <string, string>(); } }
private static void ExtractPossibleQueryParameters(HttpFunctionDefinition httpFunctionDefinition) { Debug.Assert(httpFunctionDefinition.RouteParameters != null); httpFunctionDefinition.PossibleBindingProperties = httpFunctionDefinition .CommandType .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.GetCustomAttribute <SecurityPropertyAttribute>() == null && x.SetMethod != null && (x.PropertyType == typeof(string) || x.PropertyType.GetMethods(BindingFlags.Public | BindingFlags.Static).Any(y => y.Name == "TryParse") || x.PropertyType.IsEnum || x.PropertyType.IsGenericType && x.PropertyType.GetGenericTypeDefinition() == typeof(Nullable <>)) && httpFunctionDefinition.RouteParameters.All(y => y.Name != x.Name) // we can't be a query parameter and a route parameter ) .Select(x => new HttpParameter { Name = x.Name, TypeName = x.PropertyType.EvaluateType(), Type = x.PropertyType, IsOptional = !x.PropertyType.IsValueType }) .ToArray(); }
private SyntaxTree CreateController(string namespaceName, DirectoryInfo directoryInfo, HttpFunctionDefinition httpFunctionDefinition) { string startupTemplateSource = TemplateProvider.GetTemplate("controller", "csharp"); Func <object, string> template = Handlebars.Compile(startupTemplateSource); string filenameWithoutExtension = $"{httpFunctionDefinition.Name}Controller"; string outputCode = template(httpFunctionDefinition); OutputDiagnosticCode(directoryInfo, filenameWithoutExtension, outputCode); SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(outputCode, path: $"{filenameWithoutExtension}.cs"); return(syntaxTree); }
private async Task <IActionResult> CreateValidationResponse(ICommand command, ValidationResult validationResult, HttpFunctionDefinition httpFunctionDefinition, IHttpResponseHandler responseHandler) { IActionResult actionResult = null; Task <IActionResult> validationResponseHandlerTask = responseHandler.CreateValidationFailureResponse(command, validationResult); if (validationResponseHandlerTask != null) { actionResult = await validationResponseHandlerTask; } return(actionResult ?? (CreateResponse(400, validationResult, httpFunctionDefinition))); }
public HttpFunctionConfigurationBuilder(IHttpFunctionBuilder httpFunctionBuilder, HttpFunctionDefinition definition) { _httpFunctionBuilder = httpFunctionBuilder; _definition = definition; }
private static void ExtractRouteParameters(HttpFunctionDefinition httpFunctionDefinition1) { List <HttpParameter> routeParameters = new List <HttpParameter>(); if (httpFunctionDefinition1.Route == null) { httpFunctionDefinition1.RouteParameters = routeParameters; return; } PropertyInfo[] candidateCommandProperties = httpFunctionDefinition1.CommandType .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.GetCustomAttribute <SecurityPropertyAttribute>() == null && x.SetMethod != null && (x.PropertyType == typeof(string) || Nullable.GetUnderlyingType(x.PropertyType) != null || x.PropertyType.GetMethods(BindingFlags.Public | BindingFlags.Static).Any(y => y.Name == "TryParse"))).ToArray(); Regex regex = new Regex("{(.*?)}"); MatchCollection matches = regex.Matches(httpFunctionDefinition1.Route); foreach (Match match in matches) { string routeParameter = match.Groups[1].Value; bool isOptional = routeParameter.EndsWith("?"); string[] routeParameterParts = routeParameter.Split(':'); if (routeParameterParts.Length == 0) { throw new ConfigurationException($"Bad route parameter in route {httpFunctionDefinition1.Route} for command type {httpFunctionDefinition1.CommandType.FullName}"); } string routeParameterName = routeParameterParts[0].TrimEnd('?'); PropertyInfo[] candidateProperties = candidateCommandProperties .Where(p => p.Name.ToLower() == routeParameterName.ToLower()).ToArray(); PropertyInfo matchedProperty = null; if (candidateProperties.Length == 1) { matchedProperty = candidateProperties[0]; } else if (candidateProperties.Length > 1) { matchedProperty = candidateProperties.SingleOrDefault(x => x.Name == routeParameterName); } if (matchedProperty == null) { throw new ConfigurationException($"Unable to match route parameter {routeParameterName} to property on command type {httpFunctionDefinition1.CommandType}"); } bool isPropertyNullable = !matchedProperty.PropertyType.IsValueType || Nullable.GetUnderlyingType(matchedProperty.PropertyType) != null; string routeTypeName; if (isOptional && matchedProperty.PropertyType.IsValueType && Nullable.GetUnderlyingType(matchedProperty.PropertyType) == null) { routeTypeName = $"{matchedProperty.PropertyType.EvaluateType()}?"; } else { routeTypeName = matchedProperty.PropertyType.EvaluateType(); } routeParameters.Add(new HttpParameter { Name = matchedProperty.Name, Type = matchedProperty.PropertyType, IsOptional = isOptional, IsNullableType = Nullable.GetUnderlyingType(matchedProperty.PropertyType) != null, RouteName = routeParameterName, RouteTypeName = routeTypeName }); } httpFunctionDefinition1.RouteParameters = routeParameters; /*string lowerCaseRoute = httpFunctionDefinition1.Route.ToLower(); * httpFunctionDefinition1.RouteParameters = httpFunctionDefinition1 * .CommandType * .GetProperties(BindingFlags.Instance | BindingFlags.Public) * .Where(x => x.GetCustomAttribute<SecurityPropertyAttribute>() == null * && x.SetMethod != null * && (x.PropertyType == typeof(string) || x.PropertyType * .GetMethods(BindingFlags.Public | BindingFlags.Static).Any(y => y.Name == "TryParse")) * && lowerCaseRoute.Contains("{" + x.Name.ToLower() + "}")) * .Select(x => new HttpParameter * { * Name = x.Name, * TypeName = x.PropertyType.EvaluateType(), * Type = x.PropertyType * }) * .ToArray();*/ }
public HttpFunctionBuilderMetadataBuilder(IHttpFunctionBuilder httpFunctionBuilder, HttpFunctionDefinition definition) { _httpFunctionBuilder = httpFunctionBuilder; _definition = definition; }
public HttpFunctionOptionsBuilder(HttpFunctionDefinition functionDefinition) { _functionDefinition = functionDefinition; }
public static Func <object, ClaimsPrincipal, object> Build( HttpFunctionDefinition functionDefinition, IReadOnlyCollection <AbstractClaimsMappingDefinition> claimsMappings) { if (functionDefinition.ImmutableTypeConstructorParameters.Count == 0) { return((o, cp) => o); } IReadOnlyCollection <CommandPropertyClaimsMappingDefinition> commandMappings = claimsMappings .Where(t => t is CommandPropertyClaimsMappingDefinition).Cast <CommandPropertyClaimsMappingDefinition>() .ToList(); IReadOnlyCollection <SharedClaimsMappingDefinition> sharedMappings = claimsMappings .Where(t => t is SharedClaimsMappingDefinition).Cast <SharedClaimsMappingDefinition>() .ToList(); bool didBuildAMappingFunc = false; List <Expression> constructorClaims = new List <Expression>(); ParameterExpression claimsPrincipalParameter = Expression.Parameter(typeof(ClaimsPrincipal)); ParameterExpression commandParameter = Expression.Parameter(typeof(object)); foreach (ImmutableTypeConstructorParameter constructorParameter in functionDefinition .ImmutableTypeConstructorParameters) { // there could be a shared mapping and a command -> property mapping - we use the latter first // in order of precedence CommandPropertyClaimsMappingDefinition commandPropertyDefinition = commandMappings.SingleOrDefault( x => x.CommandType == functionDefinition.CommandType && x.PropertyInfo.Name == constructorParameter.Name); if (commandPropertyDefinition != null) { constructorClaims.Add(BuildExpressionForPropertyName( commandPropertyDefinition.ClaimName, constructorParameter.Type, claimsPrincipalParameter, functionDefinition.CommandType, constructorParameter.Name, commandParameter) ); didBuildAMappingFunc = true; } else { SharedClaimsMappingDefinition sharedDefinition = sharedMappings.SingleOrDefault( x => x.PropertyPath == constructorParameter.Name); if (sharedDefinition != null) { constructorClaims.Add(BuildExpressionForPropertyName( sharedDefinition.ClaimName, constructorParameter.Type, claimsPrincipalParameter, functionDefinition.CommandType, constructorParameter.Name, commandParameter)); didBuildAMappingFunc = true; } else { constructorClaims.Add(BuildExpressionForPreviousValue(functionDefinition.CommandType, constructorParameter.Name, commandParameter)); } } } if (!didBuildAMappingFunc) { return((o, cp) => o); } ConstructorInfo constructorInfo = functionDefinition.CommandType.GetConstructor( functionDefinition.ImmutableTypeConstructorParameters.Select(x => x.Type).ToArray()); return(BuildConstructorFunc(constructorInfo, constructorClaims, commandParameter, claimsPrincipalParameter)); }