コード例 #1
0
        // CreateEndpoints processes the route pattern, replacing area/controller/action parameters with endpoint values
        // Because of default values it is possible for a route pattern to resolve to multiple endpoints
        private int CreateEndpoints(
            List <Endpoint> endpoints,
            ref StringBuilder patternStringBuilder,
            ActionDescriptor action,
            int routeOrder,
            RoutePattern routePattern,
            IReadOnlyDictionary <string, object> allDefaults,
            IReadOnlyDictionary <string, object> nonInlineDefaults,
            string name,
            RouteValueDictionary dataTokens,
            IDictionary <string, IList <IParameterPolicy> > allParameterPolicies,
            bool suppressLinkGeneration,
            bool suppressPathMatching)
        {
            var newPathSegments = routePattern.PathSegments.ToList();

            for (var i = 0; i < newPathSegments.Count; i++)
            {
                // Check if the pattern can be shortened because the remaining parameters are optional
                //
                // e.g. Matching pattern {controller=Home}/{action=Index}/{id?} against HomeController.Index
                // can resolve to the following endpoints:
                // - /Home/Index/{id?}
                // - /Home
                // - /
                if (UseDefaultValuePlusRemainingSegmentsOptional(i, action, allDefaults, newPathSegments))
                {
                    var subPathSegments = newPathSegments.Take(i);

                    var subEndpoint = CreateEndpoint(
                        action,
                        name,
                        GetPattern(ref patternStringBuilder, subPathSegments),
                        subPathSegments,
                        nonInlineDefaults,
                        routeOrder++,
                        dataTokens,
                        suppressLinkGeneration,
                        suppressPathMatching);
                    endpoints.Add(subEndpoint);
                }

                List <RoutePatternPart> segmentParts = null; // Initialize only as needed
                var segment = newPathSegments[i];
                for (var j = 0; j < segment.Parts.Count; j++)
                {
                    var part = segment.Parts[j];

                    if (part.IsParameter &&
                        part is RoutePatternParameterPart parameterPart &&
                        action.RouteValues.ContainsKey(parameterPart.Name))
                    {
                        if (segmentParts == null)
                        {
                            segmentParts = segment.Parts.ToList();
                        }
                        if (allParameterPolicies == null)
                        {
                            allParameterPolicies = MvcEndpointInfo.BuildParameterPolicies(routePattern.Parameters, _parameterPolicyFactory);
                        }

                        var parameterRouteValue = action.RouteValues[parameterPart.Name];

                        // Replace parameter with literal value
                        if (allParameterPolicies.TryGetValue(parameterPart.Name, out var parameterPolicies))
                        {
                            // Check if the parameter has a transformer policy
                            // Use the first transformer policy
                            for (var k = 0; k < parameterPolicies.Count; k++)
                            {
                                if (parameterPolicies[k] is IParameterTransformer parameterTransformer)
                                {
                                    parameterRouteValue = parameterTransformer.Transform(parameterRouteValue);
                                    break;
                                }
                            }
                        }

                        segmentParts[j] = RoutePatternFactory.LiteralPart(parameterRouteValue);
                    }
                }

                // A parameter part was replaced so replace segment with updated parts
                if (segmentParts != null)
                {
                    newPathSegments[i] = RoutePatternFactory.Segment(segmentParts);
                }
            }

            var endpoint = CreateEndpoint(
                action,
                name,
                GetPattern(ref patternStringBuilder, newPathSegments),
                newPathSegments,
                nonInlineDefaults,
                routeOrder++,
                dataTokens,
                suppressLinkGeneration,
                suppressPathMatching);

            endpoints.Add(endpoint);

            return(routeOrder);

            string GetPattern(ref StringBuilder sb, IEnumerable <RoutePatternPathSegment> segments)
            {
                if (sb == null)
                {
                    sb = new StringBuilder();
                }

                RoutePatternWriter.WriteString(sb, segments);
                var rawPattern = sb.ToString();

                sb.Length = 0;

                return(rawPattern);
            }
        }
コード例 #2
0
ファイル: MvcEndpointDataSource.cs プロジェクト: jephambl/Mvc
        private void UpdateEndpoints()
        {
            lock (_lock)
            {
                var           endpoints            = new List <Endpoint>();
                StringBuilder patternStringBuilder = null;

                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 = 0;

                        // 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 the
                        // area/controller/action parameter parts replaced with literals
                        //
                        // e.g. {controller}/{action} with HomeController.Index and HomeController.Login
                        // would result in endpoints:
                        // - Home/Index
                        // - Home/Login
                        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 isApplicable = true;
                            foreach (var routeKey in action.RouteValues.Keys)
                            {
                                if (!MatchRouteValue(action, endpointInfo, routeKey))
                                {
                                    isApplicable = false;
                                    break;
                                }
                            }

                            if (!isApplicable)
                            {
                                continue;
                            }

                            var newPathSegments = endpointInfo.ParsedPattern.PathSegments.ToList();

                            for (var i = 0; i < newPathSegments.Count; i++)
                            {
                                // Check if the pattern can be shortened because the remaining parameters are optional
                                //
                                // e.g. Matching pattern {controller=Home}/{action=Index}/{id?} against HomeController.Index
                                // can resolve to the following endpoints:
                                // - /Home/Index/{id?}
                                // - /Home
                                // - /
                                if (UseDefaultValuePlusRemainingSegmentsOptional(i, action, endpointInfo, newPathSegments))
                                {
                                    var subPathSegments = newPathSegments.Take(i);

                                    var subEndpoint = CreateEndpoint(
                                        action,
                                        endpointInfo.Name,
                                        GetPattern(ref patternStringBuilder, subPathSegments),
                                        subPathSegments,
                                        endpointInfo.Defaults,
                                        ++conventionalRouteOrder,
                                        endpointInfo,
                                        endpointInfo.DataTokens,
                                        suppressLinkGeneration: false,
                                        suppressPathMatching: false);
                                    endpoints.Add(subEndpoint);
                                }

                                List <RoutePatternPart> segmentParts = null; // Initialize only as needed
                                var segment = newPathSegments[i];
                                for (var j = 0; j < segment.Parts.Count; j++)
                                {
                                    var part = segment.Parts[j];

                                    if (part.IsParameter &&
                                        part is RoutePatternParameterPart parameterPart &&
                                        action.RouteValues.ContainsKey(parameterPart.Name))
                                    {
                                        if (segmentParts == null)
                                        {
                                            segmentParts = segment.Parts.ToList();
                                        }

                                        // Replace parameter with literal value
                                        segmentParts[j] = RoutePatternFactory.LiteralPart(action.RouteValues[parameterPart.Name]);
                                    }
                                }

                                // A parameter part was replaced so replace segment with updated parts
                                if (segmentParts != null)
                                {
                                    newPathSegments[i] = RoutePatternFactory.Segment(segmentParts);
                                }
                            }

                            var endpoint = CreateEndpoint(
                                action,
                                endpointInfo.Name,
                                GetPattern(ref patternStringBuilder, newPathSegments),
                                newPathSegments,
                                endpointInfo.Defaults,
                                ++conventionalRouteOrder,
                                endpointInfo,
                                endpointInfo.DataTokens,
                                suppressLinkGeneration: false,
                                suppressPathMatching: false);
                            endpoints.Add(endpoint);
                        }
                    }
                    else
                    {
                        var endpoint = CreateEndpoint(
                            action,
                            action.AttributeRouteInfo.Name,
                            action.AttributeRouteInfo.Template,
                            RoutePatternFactory.Parse(action.AttributeRouteInfo.Template).PathSegments,
                            nonInlineDefaults: null,
                            action.AttributeRouteInfo.Order,
                            action.AttributeRouteInfo,
                            dataTokens: null,
                            suppressLinkGeneration: action.AttributeRouteInfo.SuppressLinkGeneration,
                            suppressPathMatching: action.AttributeRouteInfo.SuppressPathMatching);
                        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();
            }

            string GetPattern(ref StringBuilder sb, IEnumerable <RoutePatternPathSegment> segments)
            {
                if (sb == null)
                {
                    sb = new StringBuilder();
                }

                RoutePatternWriter.WriteString(sb, segments);
                var rawPattern = sb.ToString();

                sb.Length = 0;

                return(rawPattern);
            }
        }
コード例 #3
0
        // CreateEndpoints processes the route pattern, replacing area/controller/action parameters with endpoint values
        // Because of default values it is possible for a route pattern to resolve to multiple endpoints
        private int CreateEndpoints(
            List <Endpoint> endpoints,
            ref StringBuilder patternStringBuilder,
            ActionDescriptor action,
            int routeOrder,
            RoutePattern routePattern,
            IReadOnlyDictionary <string, object> allDefaults,
            IReadOnlyDictionary <string, object> nonInlineDefaults,
            string name,
            RouteValueDictionary dataTokens,
            IDictionary <string, IList <IParameterPolicy> > allParameterPolicies,
            bool suppressLinkGeneration,
            bool suppressPathMatching)
        {
            var newPathSegments           = routePattern.PathSegments.ToList();
            var hasLinkGenerationEndpoint = false;

            // This is required because we create modified copies of the route pattern using its segments
            // A segment with a parameter will automatically include its policies
            // Non-parameter policies need to be manually included
            var nonParameterPolicyValues = routePattern.ParameterPolicies
                                           .Where(p => routePattern.GetParameter(p.Key ?? string.Empty) == null && p.Value.Count > 0 && p.Value.First().ParameterPolicy != null) // Only GetParameter is required. Extra is for safety
                                           .Select(p => new KeyValuePair <string, object>(p.Key, p.Value.First().ParameterPolicy))                                               // Can only pass a single non-parameter to RouteParameter
                                           .ToArray();
            var nonParameterPolicies = RouteValueDictionary.FromArray(nonParameterPolicyValues);

            // Create a mutable copy
            var nonInlineDefaultsCopy = nonInlineDefaults != null
                ? new RouteValueDictionary(nonInlineDefaults)
                : null;

            var resolvedRouteValues = ResolveActionRouteValues(action, allDefaults);

            for (var i = 0; i < newPathSegments.Count; i++)
            {
                // Check if the pattern can be shortened because the remaining parameters are optional
                //
                // e.g. Matching pattern {controller=Home}/{action=Index} against HomeController.Index
                // can resolve to the following endpoints: (sorted by RouteEndpoint.Order)
                // - /
                // - /Home
                // - /Home/Index
                if (UseDefaultValuePlusRemainingSegmentsOptional(
                        i,
                        action,
                        resolvedRouteValues,
                        allDefaults,
                        ref nonInlineDefaultsCopy,
                        newPathSegments))
                {
                    // The route pattern has matching default values AND an optional parameter
                    // For link generation we need to include an endpoint with parameters and default values
                    // so the link is correctly shortened
                    // e.g. {controller=Home}/{action=Index}/{id=17}
                    if (!hasLinkGenerationEndpoint)
                    {
                        var ep = CreateEndpoint(
                            action,
                            resolvedRouteValues,
                            name,
                            GetPattern(ref patternStringBuilder, newPathSegments),
                            nonParameterPolicies,
                            newPathSegments,
                            nonInlineDefaultsCopy,
                            routeOrder++,
                            dataTokens,
                            suppressLinkGeneration,
                            true);
                        endpoints.Add(ep);

                        hasLinkGenerationEndpoint = true;
                    }

                    var subPathSegments = newPathSegments.Take(i);

                    var subEndpoint = CreateEndpoint(
                        action,
                        resolvedRouteValues,
                        name,
                        GetPattern(ref patternStringBuilder, subPathSegments),
                        nonParameterPolicies,
                        subPathSegments,
                        nonInlineDefaultsCopy,
                        routeOrder++,
                        dataTokens,
                        suppressLinkGeneration,
                        suppressPathMatching);
                    endpoints.Add(subEndpoint);
                }

                UpdatePathSegments(i, action, resolvedRouteValues, routePattern, newPathSegments, ref allParameterPolicies);
            }

            var finalEndpoint = CreateEndpoint(
                action,
                resolvedRouteValues,
                name,
                GetPattern(ref patternStringBuilder, newPathSegments),
                nonParameterPolicies,
                newPathSegments,
                nonInlineDefaultsCopy,
                routeOrder++,
                dataTokens,
                suppressLinkGeneration,
                suppressPathMatching);

            endpoints.Add(finalEndpoint);

            return(routeOrder);

            string GetPattern(ref StringBuilder sb, IEnumerable <RoutePatternPathSegment> segments)
            {
                if (sb == null)
                {
                    sb = new StringBuilder();
                }

                RoutePatternWriter.WriteString(sb, segments);
                var rawPattern = sb.ToString();

                sb.Length = 0;

                return(rawPattern);
            }
        }
コード例 #4
0
ファイル: MvcEndpointDataSource.cs プロジェクト: whilelie/Mvc
        // CreateEndpoints processes the route pattern, replacing area/controller/action parameters with endpoint values
        // Because of default values it is possible for a route pattern to resolve to multiple endpoints
        private int CreateEndpoints(
            List <Endpoint> endpoints,
            ref StringBuilder patternStringBuilder,
            ActionDescriptor action,
            int routeOrder,
            RoutePattern routePattern,
            IReadOnlyDictionary <string, object> allDefaults,
            IReadOnlyDictionary <string, object> nonInlineDefaults,
            string name,
            RouteValueDictionary dataTokens,
            IDictionary <string, IList <IParameterPolicy> > allParameterPolicies,
            bool suppressLinkGeneration,
            bool suppressPathMatching,
            List <Action <EndpointModel> > conventions)
        {
            var newPathSegments           = routePattern.PathSegments.ToList();
            var hasLinkGenerationEndpoint = false;

            // Create a mutable copy
            var nonInlineDefaultsCopy = nonInlineDefaults != null
                ? new RouteValueDictionary(nonInlineDefaults)
                : null;

            var resolvedRouteValues = ResolveActionRouteValues(action, allDefaults);

            for (var i = 0; i < newPathSegments.Count; i++)
            {
                // Check if the pattern can be shortened because the remaining parameters are optional
                //
                // e.g. Matching pattern {controller=Home}/{action=Index} against HomeController.Index
                // can resolve to the following endpoints: (sorted by RouteEndpoint.Order)
                // - /
                // - /Home
                // - /Home/Index
                if (UseDefaultValuePlusRemainingSegmentsOptional(
                        i,
                        action,
                        resolvedRouteValues,
                        allDefaults,
                        ref nonInlineDefaultsCopy,
                        newPathSegments))
                {
                    // The route pattern has matching default values AND an optional parameter
                    // For link generation we need to include an endpoint with parameters and default values
                    // so the link is correctly shortened
                    // e.g. {controller=Home}/{action=Index}/{id=17}
                    if (!hasLinkGenerationEndpoint)
                    {
                        var ep = CreateEndpoint(
                            action,
                            resolvedRouteValues,
                            name,
                            GetPattern(ref patternStringBuilder, newPathSegments),
                            newPathSegments,
                            nonInlineDefaultsCopy,
                            routeOrder++,
                            dataTokens,
                            suppressLinkGeneration,
                            true,
                            conventions);
                        endpoints.Add(ep);

                        hasLinkGenerationEndpoint = true;
                    }

                    var subPathSegments = newPathSegments.Take(i);

                    var subEndpoint = CreateEndpoint(
                        action,
                        resolvedRouteValues,
                        name,
                        GetPattern(ref patternStringBuilder, subPathSegments),
                        subPathSegments,
                        nonInlineDefaultsCopy,
                        routeOrder++,
                        dataTokens,
                        suppressLinkGeneration,
                        suppressPathMatching,
                        conventions);
                    endpoints.Add(subEndpoint);
                }

                UpdatePathSegments(i, action, resolvedRouteValues, routePattern, newPathSegments, ref allParameterPolicies);
            }

            var finalEndpoint = CreateEndpoint(
                action,
                resolvedRouteValues,
                name,
                GetPattern(ref patternStringBuilder, newPathSegments),
                newPathSegments,
                nonInlineDefaultsCopy,
                routeOrder++,
                dataTokens,
                suppressLinkGeneration,
                suppressPathMatching,
                conventions);

            endpoints.Add(finalEndpoint);

            return(routeOrder);

            string GetPattern(ref StringBuilder sb, IEnumerable <RoutePatternPathSegment> segments)
            {
                if (sb == null)
                {
                    sb = new StringBuilder();
                }

                RoutePatternWriter.WriteString(sb, segments);
                var rawPattern = sb.ToString();

                sb.Length = 0;

                return(rawPattern);
            }
        }
コード例 #5
0
ファイル: MvcEndpointDataSource.cs プロジェクト: ilmax/Mvc
        private List <Endpoint> CreateEndpoints()
        {
            var           endpoints            = new List <Endpoint>();
            StringBuilder patternStringBuilder = null;

            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 = 0;

                    // 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 the
                    // area/controller/action parameter parts replaced with literals
                    //
                    // e.g. {controller}/{action} with HomeController.Index and HomeController.Login
                    // would result in endpoints:
                    // - Home/Index
                    // - Home/Login
                    foreach (var endpointInfo in ConventionalEndpointInfos)
                    {
                        if (MatchRouteValue(action, endpointInfo, "Area") &&
                            MatchRouteValue(action, endpointInfo, "Controller") &&
                            MatchRouteValue(action, endpointInfo, "Action"))
                        {
                            var newPathSegments = endpointInfo.ParsedPattern.PathSegments.ToList();

                            for (var i = 0; i < newPathSegments.Count; i++)
                            {
                                // Check if the pattern can be shortened because the remaining parameters are optional
                                //
                                // e.g. Matching pattern {controller=Home}/{action=Index}/{id?} against HomeController.Index
                                // can resolve to the following endpoints:
                                // - /Home/Index/{id?}
                                // - /Home
                                // - /
                                if (UseDefaultValuePlusRemainingSegementsOptional(i, action, endpointInfo, newPathSegments))
                                {
                                    var subPathSegments = newPathSegments.Take(i);

                                    var subEndpoint = CreateEndpoint(
                                        action,
                                        endpointInfo.Name,
                                        GetPattern(ref patternStringBuilder, subPathSegments),
                                        subPathSegments,
                                        endpointInfo.Defaults,
                                        ++conventionalRouteOrder,
                                        endpointInfo,
                                        suppressLinkGeneration: false);
                                    endpoints.Add(subEndpoint);
                                }

                                List <RoutePatternPart> segmentParts = null; // Initialize only as needed
                                var segment = newPathSegments[i];
                                for (var j = 0; j < segment.Parts.Count; j++)
                                {
                                    var part = segment.Parts[j];

                                    if (part.IsParameter && part is RoutePatternParameterPart parameterPart && IsMvcParameter(parameterPart.Name))
                                    {
                                        if (segmentParts == null)
                                        {
                                            segmentParts = segment.Parts.ToList();
                                        }

                                        // Replace parameter with literal value
                                        segmentParts[j] = RoutePatternFactory.LiteralPart(action.RouteValues[parameterPart.Name]);
                                    }
                                }

                                // A parameter part was replaced so replace segment with updated parts
                                if (segmentParts != null)
                                {
                                    newPathSegments[i] = RoutePatternFactory.Segment(segmentParts);
                                }
                            }

                            var endpoint = CreateEndpoint(
                                action,
                                endpointInfo.Name,
                                GetPattern(ref patternStringBuilder, newPathSegments),
                                newPathSegments,
                                endpointInfo.Defaults,
                                ++conventionalRouteOrder,
                                endpointInfo,
                                suppressLinkGeneration: false);
                            endpoints.Add(endpoint);
                        }
                    }
                }
                else
                {
                    var endpoint = CreateEndpoint(
                        action,
                        action.AttributeRouteInfo.Name,
                        action.AttributeRouteInfo.Template,
                        RoutePatternFactory.Parse(action.AttributeRouteInfo.Template).PathSegments,
                        nonInlineDefaults: null,
                        action.AttributeRouteInfo.Order,
                        action.AttributeRouteInfo,
                        suppressLinkGeneration: action.AttributeRouteInfo.SuppressLinkGeneration);
                    endpoints.Add(endpoint);
                }
            }

            return(endpoints);

            string GetPattern(ref StringBuilder sb, IEnumerable <RoutePatternPathSegment> segments)
            {
                if (sb == null)
                {
                    sb = new StringBuilder();
                }

                RoutePatternWriter.WriteString(sb, segments);
                var rawPattern = sb.ToString();

                sb.Length = 0;

                return(rawPattern);
            }
        }