private List <Endpoint> BuildEndpoints() { List <Endpoint> endpoints = new List <Endpoint>(); foreach (var hubMethod in HubMethods) { var requiredValues = new { hub = hubMethod.Hub, method = hubMethod.Method }; var order = 1; foreach (var pattern in Patterns) { var resolvedPattern = _routePatternTransformer.SubstituteRequiredValues(pattern, requiredValues); if (resolvedPattern == null) { continue; } var endpointModel = new RouteEndpointModel( hubMethod.RequestDelegate, resolvedPattern, order++); endpointModel.DisplayName = $"{hubMethod.Hub}.{hubMethod.Method}"; foreach (var convention in _conventions) { convention(endpointModel); } endpoints.Add(endpointModel.Build()); } } return(endpoints); }
private RouteEndpoint CreateEndpoint( ActionDescriptor action, IDictionary <string, string> actionRouteValues, string routeName, string patternRawText, IEnumerable <RoutePatternPathSegment> segments, object nonInlineDefaults, int order, RouteValueDictionary dataTokens, bool suppressLinkGeneration, bool suppressPathMatching, List <Action <EndpointModel> > conventions) { RequestDelegate requestDelegate = (context) => { var routeData = context.GetRouteData(); var actionContext = new ActionContext(context, routeData, action); var invoker = _invokerFactory.CreateInvoker(actionContext); return(invoker.InvokeAsync()); }; var defaults = new RouteValueDictionary(nonInlineDefaults); EnsureRequiredValuesInDefaults(actionRouteValues, defaults, segments); var model = new RouteEndpointModel(requestDelegate, RoutePatternFactory.Pattern(patternRawText, defaults, parameterPolicies: null, segments), order); AddEndpointMetadata( model.Metadata, action, routeName, new RouteValueDictionary(actionRouteValues), dataTokens, suppressLinkGeneration, suppressPathMatching); model.DisplayName = action.DisplayName; // REVIEW: When should conventions be run // Metadata should have lower precedence that data source metadata if (conventions != null) { foreach (var convention in conventions) { convention(model); } } return((RouteEndpoint)model.Build()); }
private RouteEndpoint CreateEndpoint( ActionDescriptor action, RoutePattern routePattern, string routeName, int order, RouteValueDictionary dataTokens, bool suppressLinkGeneration, bool suppressPathMatching, List <Action <EndpointModel> > conventions) { RequestDelegate requestDelegate = (context) => { var routeData = context.GetRouteData(); var actionContext = new ActionContext(context, routeData, action); var invoker = _invokerFactory.CreateInvoker(actionContext); return(invoker.InvokeAsync()); }; var model = new RouteEndpointModel(requestDelegate, routePattern, order); AddEndpointMetadata( model.Metadata, action, routeName, dataTokens, suppressLinkGeneration, suppressPathMatching); model.DisplayName = action.DisplayName; // REVIEW: When should conventions be run // Metadata should have lower precedence that data source metadata if (conventions != null) { foreach (var convention in conventions) { convention(model); } } return((RouteEndpoint)model.Build()); }
private static List <Endpoint> BuildWithoutCache(Type handlerType, Action <HttpModel> configure) { var model = HttpModel.FromType(handlerType); configure?.Invoke(model); var endpoints = new List <Endpoint>(); foreach (var method in model.Methods.Where(m => m.RoutePattern != null)) { var needForm = false; var httpMethod = method.HttpMethod; var template = method.RoutePattern; // Non void return type // Task Invoke(HttpContext httpContext, RouteValueDictionary routeValues, RequestDelegate next) // { // // The type is activated via DI if it has args // return ExecuteResult(new THttpHandler(...).Method(..), httpContext); // } // void return type // Task Invoke(HttpContext httpContext, RouteValueDictionary routeValues, RequestDelegate next) // { // new THttpHandler(...).Method(..) // return Task.CompletedTask; // } var httpContextArg = Expression.Parameter(typeof(HttpContext), "httpContext"); var routeValuesArg = Expression.Parameter(typeof(RouteValueDictionary), "routeValues"); var nextArg = Expression.Parameter(typeof(RequestDelegate), "next"); var requestServicesExpr = Expression.Property(httpContextArg, nameof(HttpContext.RequestServices)); // Fast path: We can skip the activator if there's only a default ctor with 0 args var ctors = handlerType.GetConstructors(); Expression httpHandlerExpression = null; if (method.MethodInfo.IsStatic) { // Do nothing } else if (ctors.Length == 1 && ctors[0].GetParameters().Length == 0) { httpHandlerExpression = Expression.New(ctors[0]); } else { // CreateInstance<THttpHandler>(context.RequestServices) httpHandlerExpression = Expression.Call(ActivatorMethodInfo.MakeGenericMethod(handlerType), requestServicesExpr); } var args = new List <Expression>(); var httpRequestExpr = Expression.Property(httpContextArg, nameof(HttpContext.Request)); foreach (var parameter in method.Parameters) { Expression paramterExpression = Expression.Default(parameter.ParameterType); if (parameter.FromQuery != null) { var queryProperty = Expression.Property(httpRequestExpr, nameof(HttpRequest.Query)); paramterExpression = BindArgument(queryProperty, parameter, parameter.FromQuery); } else if (parameter.FromHeader != null) { var headersProperty = Expression.Property(httpRequestExpr, nameof(HttpRequest.Headers)); paramterExpression = BindArgument(headersProperty, parameter, parameter.FromHeader); } else if (parameter.FromRoute != null) { paramterExpression = BindArgument(routeValuesArg, parameter, parameter.FromRoute); } else if (parameter.FromCookie != null) { var cookiesProperty = Expression.Property(httpRequestExpr, nameof(HttpRequest.Cookies)); paramterExpression = BindArgument(cookiesProperty, parameter, parameter.FromCookie); } else if (parameter.FromServices) { paramterExpression = Expression.Call(GetRequiredServiceMethodInfo.MakeGenericMethod(parameter.ParameterType), requestServicesExpr); } else if (parameter.FromForm != null) { needForm = true; var formProperty = Expression.Property(httpRequestExpr, nameof(HttpRequest.Form)); paramterExpression = BindArgument(formProperty, parameter, parameter.FromForm); } else if (parameter.FromBody) { var bodyProperty = Expression.Property(httpRequestExpr, nameof(HttpRequest.Body)); paramterExpression = BindBody(bodyProperty, parameter); } else { if (parameter.ParameterType == typeof(IFormCollection)) { paramterExpression = Expression.Property(httpRequestExpr, nameof(HttpRequest.Form)); } else if (parameter.ParameterType == typeof(HttpContext)) { paramterExpression = httpContextArg; } else if (parameter.ParameterType == typeof(RequestDelegate)) { paramterExpression = nextArg; } else if (parameter.ParameterType == typeof(IHeaderDictionary)) { paramterExpression = Expression.Property(httpRequestExpr, nameof(HttpRequest.Headers)); } } args.Add(paramterExpression); } Expression body = null; if (method.ReturnType == typeof(void)) { var bodyExpressions = new List <Expression> { Expression.Call(httpHandlerExpression, method.MethodInfo, args), Expression.Property(null, (PropertyInfo)CompletedTaskMemberInfo) }; body = Expression.Block(bodyExpressions); } else { var methodCall = Expression.Call(httpHandlerExpression, method.MethodInfo, args); // Coerce Task<T> to Task<object> if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task <>)) { var typeArg = method.ReturnType.GetGenericArguments()[0]; // ExecuteTask<T>(handler.Method(..), httpContext); body = Expression.Call( ExecuteTaskOfTMethodInfo.MakeGenericMethod(typeArg), methodCall, httpContextArg); } else { // ExecuteResult(handler.Method(..), httpContext); body = Expression.Call(ExecuteAsyncMethodInfo, methodCall, httpContextArg); } } var lambda = Expression.Lambda <Func <HttpContext, RouteValueDictionary, RequestDelegate, Task> >(body, httpContextArg, routeValuesArg, nextArg); var routeTemplate = method.RoutePattern; var invoker = lambda.Compile(); var routeEndpointModel = new RouteEndpointModel( async httpContext => { // Generating async code would just be insane so if the method needs the form populate it here // so the within the method it's cached if (needForm) { await httpContext.Request.ReadFormAsync(); } await invoker.Invoke(httpContext, httpContext.Request.RouteValues, (c) => Task.CompletedTask); }, routeTemplate, 0); routeEndpointModel.DisplayName = routeTemplate.RawText; if (!string.IsNullOrEmpty(method.HttpMethod)) { routeEndpointModel.Metadata.Add(new HttpMethodMetadata(new[] { method.HttpMethod })); } foreach (var attribute in method.MethodInfo.GetCustomAttributes(true)) { routeEndpointModel.Metadata.Add(attribute); } foreach (var convention in method.Conventions) { convention(routeEndpointModel); } endpoints.Add(routeEndpointModel.Build()); } return(endpoints); }