예제 #1
0
        protected override List <Endpoint> CreateEndpoints(IReadOnlyList <ActionDescriptor> actions, IReadOnlyList <Action <EndpointBuilder> > conventions)
        {
            var endpoints = new List <Endpoint>();

            foreach (var action in actions)
            {
                var attributeInfo = action.AttributeRouteInfo;
                if (attributeInfo == null) //Conventional Routing
                {
                    foreach (var route in _conventionalRoutes)
                    {
                        var pattern = _routePatternTransformer.SubstituteRequiredValues(route.Pattern, action.RouteValues);
                        if (pattern != null)
                        {
                            RouteEndpointBuilder builder = new RouteEndpointBuilder(_requestDelegate, pattern, route.Order);
                            builder.Metadata.Add(action);
                            endpoints.Add(builder.Build());
                        }
                    }
                }
                else //Attribute Routing
                {
                    var original = RoutePatternFactory.Parse(attributeInfo.Template);
                    var pattern  = _routePatternTransformer.SubstituteRequiredValues(original, action.RouteValues);
                    if (pattern != null)
                    {
                        RouteEndpointBuilder builder = new RouteEndpointBuilder(_requestDelegate, pattern, attributeInfo.Order);
                        builder.Metadata.Add(action);
                        endpoints.Add(builder.Build());
                    }
                }
            }
            return(endpoints);
        }
        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);
        }
예제 #3
0
        public MvcEndpointDataSource(IActionInvokerFactory actionInvokerFactory,
                                     IActionDescriptorProvider actionDiscriptorProvider,
                                     RoutePatternTransformer transformer)
        {
            _actionInvokerFactory = actionInvokerFactory;

            _endpoints = new Lazy <IReadOnlyList <Endpoint> >(Create());

            List <Endpoint> Create()
            {
                var Descriptors = actionDiscriptorProvider.GetActionDescriptors;

                return(Descriptors.Select(CreateEndpoint).ToList());
            }

            Endpoint CreateEndpoint(ActionDescriptor actionDiscriptor)
            {
                var routePattern    = RoutePatternFactory.Parse(actionDiscriptor.RouteInfo.Template);
                var newRoutePattern = transformer.SubstituteRequiredValues(routePattern, new Dictionary <string, string>
                {
                    ["controller"] = actionDiscriptor.ControllerName,
                    ["action"]     = actionDiscriptor.ActionName
                });

                routePattern = routePattern ?? newRoutePattern;
                var endPointBuilder = new RouteEndpointBuilder(InvokeAsync, routePattern, actionDiscriptor.RouteInfo.Order ?? 0);

                endPointBuilder.Metadata.Add(actionDiscriptor);
                return(endPointBuilder.Build());
            }
        }
    public void AddEndpoints(
        List <Endpoint> endpoints,
        HashSet <string> routeNames,
        ActionDescriptor action,
        IReadOnlyList <ConventionalRouteEntry> routes,
        IReadOnlyList <Action <EndpointBuilder> > conventions,
        bool createInertEndpoints)
    {
        if (endpoints == null)
        {
            throw new ArgumentNullException(nameof(endpoints));
        }

        if (routeNames == null)
        {
            throw new ArgumentNullException(nameof(routeNames));
        }

        if (action == null)
        {
            throw new ArgumentNullException(nameof(action));
        }

        if (routes == null)
        {
            throw new ArgumentNullException(nameof(routes));
        }

        if (conventions == null)
        {
            throw new ArgumentNullException(nameof(conventions));
        }

        if (createInertEndpoints)
        {
            var builder = new InertEndpointBuilder()
            {
                DisplayName     = action.DisplayName,
                RequestDelegate = _requestDelegate,
            };
            AddActionDataToBuilder(
                builder,
                routeNames,
                action,
                routeName: null,
                dataTokens: null,
                suppressLinkGeneration: false,
                suppressPathMatching: false,
                conventions,
                Array.Empty <Action <EndpointBuilder> >());
            endpoints.Add(builder.Build());
        }

        if (action.AttributeRouteInfo?.Template == null)
        {
            // Check each of the conventional patterns to see if the action would be reachable.
            // If the action and pattern are compatible then create an endpoint with action
            // route values on the pattern.
            foreach (var route in routes)
            {
                // A route is applicable if:
                // 1. It has a parameter (or default value) for 'required' non-null route value
                // 2. It does not have a parameter (or default value) for 'required' null route value
                var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(route.Pattern, action.RouteValues);
                if (updatedRoutePattern == null)
                {
                    continue;
                }

                var requestDelegate = CreateRequestDelegate(action, route.DataTokens) ?? _requestDelegate;

                // We suppress link generation for each conventionally routed endpoint. We generate a single endpoint per-route
                // to handle link generation.
                var builder = new RouteEndpointBuilder(requestDelegate, updatedRoutePattern, route.Order)
                {
                    DisplayName = action.DisplayName,
                };
                AddActionDataToBuilder(
                    builder,
                    routeNames,
                    action,
                    route.RouteName,
                    route.DataTokens,
                    suppressLinkGeneration: true,
                    suppressPathMatching: false,
                    conventions,
                    route.Conventions);
                endpoints.Add(builder.Build());
            }
        }
        else
        {
            var requestDelegate       = CreateRequestDelegate(action) ?? _requestDelegate;
            var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template);

            // Modify the route and required values to ensure required values can be successfully subsituted.
            // Subsitituting required values into an attribute route pattern should always succeed.
            var(resolvedRoutePattern, resolvedRouteValues) = ResolveDefaultsAndRequiredValues(action, attributeRoutePattern);

            var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues);
            if (updatedRoutePattern == null)
            {
                // This kind of thing can happen when a route pattern uses a *reserved* route value such as `action`.
                // See: https://github.com/dotnet/aspnetcore/issues/14789
                var formattedRouteKeys = string.Join(", ", resolvedRouteValues.Keys.Select(k => $"'{k}'"));
                throw new InvalidOperationException(
                          $"Failed to update the route pattern '{resolvedRoutePattern.RawText}' with required route values. " +
                          $"This can occur when the route pattern contains parameters with reserved names such as: {formattedRouteKeys} " +
                          $"and also uses route constraints such as '{{action:int}}'. " +
                          "To fix this error, choose a different parameter name.");
            }

            var builder = new RouteEndpointBuilder(requestDelegate, updatedRoutePattern, action.AttributeRouteInfo.Order)
            {
                DisplayName = action.DisplayName,
            };
            AddActionDataToBuilder(
                builder,
                routeNames,
                action,
                action.AttributeRouteInfo.Name,
                dataTokens: null,
                action.AttributeRouteInfo.SuppressLinkGeneration,
                action.AttributeRouteInfo.SuppressPathMatching,
                conventions,
                perRouteConventions: Array.Empty <Action <EndpointBuilder> >());
            endpoints.Add(builder.Build());
        }
    }
        public void AddEndpoints(
            List <Endpoint> endpoints,
            HashSet <string> routeNames,
            ActionDescriptor action,
            IReadOnlyList <ConventionalRouteEntry> routes,
            IReadOnlyList <Action <EndpointBuilder> > conventions)
        {
            if (endpoints == null)
            {
                throw new ArgumentNullException(nameof(endpoints));
            }

            if (routeNames == null)
            {
                throw new ArgumentNullException(nameof(routeNames));
            }

            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

            if (routes == null)
            {
                throw new ArgumentNullException(nameof(routes));
            }

            if (conventions == null)
            {
                throw new ArgumentNullException(nameof(conventions));
            }

            if (action.AttributeRouteInfo == null)
            {
                // Check each of the conventional patterns to see if the action would be reachable.
                // If the action and pattern are compatible then create an endpoint with action
                // route values on the pattern.
                foreach (var route in routes)
                {
                    // A route is applicable if:
                    // 1. It has a parameter (or default value) for 'required' non-null route value
                    // 2. It does not have a parameter (or default value) for 'required' null route value
                    var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(route.Pattern, action.RouteValues);
                    if (updatedRoutePattern == null)
                    {
                        continue;
                    }

                    // We suppress link generation for each conventionally routed endpoint. We generate a single endpoint per-route
                    // to handle link generation.
                    var builder = CreateEndpoint(
                        routeNames,
                        action,
                        updatedRoutePattern,
                        route.RouteName,
                        route.Order,
                        route.DataTokens,
                        suppressLinkGeneration: true,
                        suppressPathMatching: false,
                        conventions,
                        route.Conventions);
                    endpoints.Add(builder);
                }
            }
            else
            {
                var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template);

                // Modify the route and required values to ensure required values can be successfully subsituted.
                // Subsitituting required values into an attribute route pattern should always succeed.
                var(resolvedRoutePattern, resolvedRouteValues) = ResolveDefaultsAndRequiredValues(action, attributeRoutePattern);

                var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues);
                if (updatedRoutePattern == null)
                {
                    throw new InvalidOperationException("Failed to update route pattern with required values.");
                }

                var endpoint = CreateEndpoint(
                    routeNames,
                    action,
                    updatedRoutePattern,
                    action.AttributeRouteInfo.Name,
                    action.AttributeRouteInfo.Order,
                    dataTokens: null,
                    action.AttributeRouteInfo.SuppressLinkGeneration,
                    action.AttributeRouteInfo.SuppressPathMatching,
                    conventions,
                    perRouteConventions: Array.Empty <Action <EndpointBuilder> >());
                endpoints.Add(endpoint);
            }
        }
예제 #6
0
        public void AddEndpoints(
            List <Endpoint> endpoints,
            ActionDescriptor action,
            IReadOnlyList <ConventionalRouteEntry> routes,
            IReadOnlyList <Action <EndpointBuilder> > conventions)
        {
            if (endpoints == null)
            {
                throw new ArgumentNullException(nameof(endpoints));
            }

            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

            if (routes == null)
            {
                throw new ArgumentNullException(nameof(routes));
            }

            if (conventions == null)
            {
                throw new ArgumentNullException(nameof(conventions));
            }

            if (action.AttributeRouteInfo == null)
            {
                // In traditional conventional routing setup, the routes defined by a user have a static order
                // defined by how they are added into the list. We would like to maintain the same order when building
                // up the endpoints too.
                //
                // Start with an order of '1' for conventional routes as attribute routes have a default order of '0'.
                // This is for scenarios dealing with migrating existing Router based code to Endpoint Routing world.
                var conventionalRouteOrder = 1;

                // Check each of the conventional patterns to see if the action would be reachable.
                // If the action and pattern are compatible then create an endpoint with action
                // route values on the pattern.
                foreach (var route in routes)
                {
                    // A route is applicable if:
                    // 1. It has a parameter (or default value) for 'required' non-null route value
                    // 2. It does not have a parameter (or default value) for 'required' null route value
                    var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(route.Pattern, action.RouteValues);
                    if (updatedRoutePattern == null)
                    {
                        continue;
                    }

                    var builder = CreateEndpoint(
                        action,
                        updatedRoutePattern,
                        route.RouteName,
                        conventionalRouteOrder++,
                        route.DataTokens,
                        suppressLinkGeneration: false,
                        suppressPathMatching: false,
                        conventions);
                    endpoints.Add(builder);
                }
            }
            else
            {
                var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template);

                // Modify the route and required values to ensure required values can be successfully subsituted.
                // Subsitituting required values into an attribute route pattern should always succeed.
                var(resolvedRoutePattern, resolvedRouteValues) = ResolveDefaultsAndRequiredValues(action, attributeRoutePattern);

                var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues);
                if (updatedRoutePattern == null)
                {
                    throw new InvalidOperationException("Failed to update route pattern with required values.");
                }

                var endpoint = CreateEndpoint(
                    action,
                    updatedRoutePattern,
                    action.AttributeRouteInfo.Name,
                    action.AttributeRouteInfo.Order,
                    dataTokens: null,
                    action.AttributeRouteInfo.SuppressLinkGeneration,
                    action.AttributeRouteInfo.SuppressPathMatching,
                    conventions);
                endpoints.Add(endpoint);
            }
        }
        private void UpdateEndpoints()
        {
            lock (_lock)
            {
                var endpoints = new List <Endpoint>();

                foreach (var action in _actions.ActionDescriptors.Items)
                {
                    if (action.AttributeRouteInfo == null)
                    {
                        // In traditional conventional routing setup, the routes defined by a user have a static order
                        // defined by how they are added into the list. We would like to maintain the same order when building
                        // up the endpoints too.
                        //
                        // Start with an order of '1' for conventional routes as attribute routes have a default order of '0'.
                        // This is for scenarios dealing with migrating existing Router based code to Endpoint Routing world.
                        var conventionalRouteOrder = 1;

                        // Check each of the conventional patterns to see if the action would be reachable.
                        // If the action and pattern are compatible then create an endpoint with action
                        // route values on the pattern.
                        foreach (var endpointInfo in ConventionalEndpointInfos)
                        {
                            // An 'endpointInfo' is applicable if:
                            // 1. It has a parameter (or default value) for 'required' non-null route value
                            // 2. It does not have a parameter (or default value) for 'required' null route value
                            var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(endpointInfo.ParsedPattern, action.RouteValues);

                            if (updatedRoutePattern == null)
                            {
                                continue;
                            }

                            var endpoint = CreateEndpoint(
                                action,
                                updatedRoutePattern,
                                endpointInfo.Name,
                                conventionalRouteOrder++,
                                endpointInfo.DataTokens,
                                false,
                                false,
                                endpointInfo.Conventions);
                            endpoints.Add(endpoint);
                        }
                    }
                    else
                    {
                        var conventionBuilder = ResolveActionConventionBuilder(action);
                        if (conventionBuilder == null)
                        {
                            // No convention builder for this action
                            // Do not create an endpoint for it
                            continue;
                        }

                        var attributeRoutePattern = RoutePatternFactory.Parse(action.AttributeRouteInfo.Template);

                        // Modify the route and required values to ensure required values can be successfully subsituted.
                        // Subsitituting required values into an attribute route pattern should always succeed.
                        var(resolvedRoutePattern, resolvedRouteValues) = ResolveDefaultsAndRequiredValues(action, attributeRoutePattern);

                        var updatedRoutePattern = _routePatternTransformer.SubstituteRequiredValues(resolvedRoutePattern, resolvedRouteValues);

                        if (updatedRoutePattern == null)
                        {
                            throw new InvalidOperationException("Failed to update route pattern with required values.");
                        }

                        var endpoint = CreateEndpoint(
                            action,
                            updatedRoutePattern,
                            action.AttributeRouteInfo.Name,
                            action.AttributeRouteInfo.Order,
                            dataTokens: null,
                            action.AttributeRouteInfo.SuppressLinkGeneration,
                            action.AttributeRouteInfo.SuppressPathMatching,
                            conventionBuilder.Conventions);
                        endpoints.Add(endpoint);
                    }
                }

                // See comments in DefaultActionDescriptorCollectionProvider. These steps are done
                // in a specific order to ensure callers always see a consistent state.

                // Step 1 - capture old token
                var oldCancellationTokenSource = _cancellationTokenSource;

                // Step 2 - update endpoints
                _endpoints = endpoints;

                // Step 3 - create new change token
                _cancellationTokenSource = new CancellationTokenSource();
                _changeToken             = new CancellationChangeToken(_cancellationTokenSource.Token);

                // Step 4 - trigger old token
                oldCancellationTokenSource?.Cancel();
            }
        }