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);
        }
Example #2
0
        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());
        }
Example #4
0
        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);
        }