示例#1
0
        public void GetValues_SuccessfullyMatchesRouteValues_ForExplicitNullValue_AndEmptyStringDefault()
        {
            // Arrange
            var expected       = "/Home/Index";
            var template       = "Home/Index";
            var defaults       = new RouteValueDictionary(new { controller = "Home", action = "Index", area = "" });
            var ambientValues  = new RouteValueDictionary(new { controller = "Rail", action = "Schedule", area = "Travel" });
            var explicitValues = new RouteValueDictionary(new { controller = "Home", action = "Index", area = (string)null });
            var binder         = new TemplateBinder(
                UrlEncoder.Default,
                new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
                TemplateParser.Parse(template),
                defaults);

            // Act1
            var result = binder.GetValues(ambientValues, explicitValues);

            // Assert1
            Assert.NotNull(result);

            // Act2
            var boundTemplate = binder.BindValues(result.AcceptedValues);

            // Assert2
            Assert.NotNull(boundTemplate);
            Assert.Equal(expected, boundTemplate);
        }
示例#2
0
        private void RunTest(
            string template,
            RouteValueDictionary defaults,
            RouteValueDictionary ambientValues,
            RouteValueDictionary values,
            string expected,
            UrlEncoder encoder = null)
        {
            // Arrange
            var binderFactory = encoder == null ? BinderFactory : new RoutePatternBinderFactory(encoder, new DefaultObjectPoolProvider());
            var binder        = new TemplateBinder(binderFactory.Create(template, defaults ?? new RouteValueDictionary()));

            // Act & Assert
            var result = binder.GetValues(ambientValues, values);

            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);

            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);

                // We want to chop off the query string and compare that using an unordered comparison
                var expectedParts = new PathAndQuery(expected);
                var actualParts   = new PathAndQuery(boundTemplate);

                Assert.Equal(expectedParts.Path, actualParts.Path);

                if (expectedParts.Parameters == null)
                {
                    Assert.Null(actualParts.Parameters);
                }
                else
                {
                    Assert.Equal(expectedParts.Parameters.Count, actualParts.Parameters.Count);

                    foreach (var kvp in expectedParts.Parameters)
                    {
                        string value;
                        Assert.True(actualParts.Parameters.TryGetValue(kvp.Key, out value));
                        Assert.Equal(kvp.Value, value);
                    }
                }
            }
        }
        public void TemplateBinder_KeepsExplicitlySuppliedRouteValues_OnFailedRouetMatch()
        {
            // Arrange
            var template = "{area?}/{controller=Home}/{action=Index}/{id?}";
            var binder   = new TemplateBinder(
                UrlEncoder.Default,
                new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
                TemplateParser.Parse(template),
                defaults: null);
            var ambientValues = new RouteValueDictionary();
            var routeValues   = new RouteValueDictionary(new { controller = "Test", action = "Index" });

            // Act
            var templateValuesResult = binder.GetValues(ambientValues, routeValues);
            var boundTemplate        = binder.BindValues(templateValuesResult.AcceptedValues);

            // Assert
            Assert.Null(boundTemplate);
            Assert.Equal(2, templateValuesResult.CombinedValues.Count);
            object routeValue;

            Assert.True(templateValuesResult.CombinedValues.TryGetValue("controller", out routeValue));
            Assert.Equal("Test", routeValue?.ToString());
            Assert.True(templateValuesResult.CombinedValues.TryGetValue("action", out routeValue));
            Assert.Equal("Index", routeValue?.ToString());
        }
示例#4
0
        private string GetLink(MatcherEndpoint endpoint, LinkGeneratorContext context)
        {
            var templateBinder = new TemplateBinder(
                UrlEncoder.Default,
                _uriBuildingContextPool,
                new RouteTemplate(endpoint.RoutePattern),
                new RouteValueDictionary(endpoint.RoutePattern.Defaults));

            var templateValuesResult = templateBinder.GetValues(
                ambientValues: context.AmbientValues,
                explicitValues: context.ExplicitValues,
                endpoint.RequiredValues.Keys);

            if (templateValuesResult == null)
            {
                // We're missing one of the required values for this route.
                return(null);
            }

            if (!MatchesConstraints(context.HttpContext, endpoint, templateValuesResult.CombinedValues))
            {
                return(null);
            }

            var url = templateBinder.BindValues(templateValuesResult.AcceptedValues);

            return(Normalize(context, url));
        }
示例#5
0
        internal string MakeLink(
            HttpContext httpContext,
            RouteEndpoint endpoint,
            RouteValueDictionary ambientValues,
            RouteValueDictionary explicitValues,
            LinkOptions options)
        {
            var templateBinder = new TemplateBinder(
                UrlEncoder.Default,
                _uriBuildingContextPool,
                endpoint.RoutePattern,
                new RouteValueDictionary(endpoint.RoutePattern.Defaults));

            var routeValuesAddressMetadata = endpoint.Metadata.GetMetadata <IRouteValuesAddressMetadata>();
            var templateValuesResult       = templateBinder.GetValues(
                ambientValues: ambientValues,
                explicitValues: explicitValues,
                requiredKeys: routeValuesAddressMetadata?.RequiredValues.Keys);

            if (templateValuesResult == null)
            {
                // We're missing one of the required values for this route.
                return(null);
            }

            if (!MatchesConstraints(httpContext, endpoint, templateValuesResult.CombinedValues))
            {
                return(null);
            }

            var url = templateBinder.BindValues(templateValuesResult.AcceptedValues);

            return(Normalize(url, options));
        }
示例#6
0
        private static void RunTest(
            string template,
            IReadOnlyDictionary <string, object> defaults,
            IDictionary <string, object> ambientValues,
            IDictionary <string, object> values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(TemplateParser.Parse(template), defaults);

            // Act & Assert
            var result = binder.GetValues(ambientValues, values);

            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);

            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);

                // We want to chop off the query string and compare that using an unordered comparison
                var expectedParts = new PathAndQuery(expected);
                var actualParts   = new PathAndQuery(boundTemplate);

                Assert.Equal(expectedParts.Path, actualParts.Path);

                if (expectedParts.Parameters == null)
                {
                    Assert.Null(actualParts.Parameters);
                }
                else
                {
                    Assert.Equal(expectedParts.Parameters.Count, actualParts.Parameters.Count);

                    foreach (var kvp in expectedParts.Parameters)
                    {
                        string value;
                        Assert.True(actualParts.Parameters.TryGetValue(kvp.Key, out value));
                        Assert.Equal(kvp.Value, value);
                    }
                }
            }
        }
示例#7
0
        /// <inheritdoc />
        public virtual VirtualPathData GetVirtualPath(VirtualPathContext context)
        {
            EnsureBinder(context.HttpContext);
            EnsureLoggers(context.HttpContext);

            var values = _binder.GetValues(context.AmbientValues, context.Values);

            if (values == null)
            {
                // We're missing one of the required values for this route.
                return(null);
            }

            if (!RouteConstraintMatcher.Match(
                    Constraints,
                    values.CombinedValues,
                    context.HttpContext,
                    this,
                    RouteDirection.UrlGeneration,
                    _logger
                    ))
            {
                return(null);
            }

            context.Values = values.CombinedValues;

            var pathData = OnVirtualPathGenerated(context);

            if (pathData != null)
            {
                // If the target generates a value then that can short circuit.
                return(pathData);
            }

            // If we can produce a value go ahead and do it, the caller can check context.IsBound
            // to see if the values were validated.

            // When we still cannot produce a value, this should return null.
            var virtualPath = _binder.BindValues(values.AcceptedValues);

            if (virtualPath == null)
            {
                return(null);
            }

            pathData = new VirtualPathData(this, virtualPath);
            if (DataTokens != null)
            {
                foreach (var dataToken in DataTokens)
                {
                    pathData.DataTokens.Add(dataToken.Key, dataToken.Value);
                }
            }

            return(pathData);
        }
示例#8
0
        /// <summary>
        /// 使用mvc路由模版绑定参数
        /// </summary>
        /// <param name="template">路由模版</param>
        /// <param name="values">参数</param>
        /// <returns></returns>
        private string BoundTemplate(string template, RouteValueDictionary values)
        {
            var binder = new TemplateBinder(
                UrlEncoder.Default,
                new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy(UrlEncoder.Default)),
                TemplateParser.Parse(template),
                null
                );

            // Act & Assert
            var result = binder.GetValues(new RouteValueDictionary(), values);

            return(binder.BindValues(result.AcceptedValues));
        }
示例#9
0
        /// <summary>
        /// 使用mvc路由模版绑定参数
        /// </summary>
        /// <param name="template">路由模版</param>
        /// <param name="values">参数</param>
        /// <returns></returns>
        private string BoundTemplate(string template, RouteValueDictionary values)
        {
            var binder = new TemplateBinder(
                UrlEncoder.Default,
                null,
                TemplateParser.Parse(template),
                null
                );

            // Act & Assert
            var result = binder.GetValues(new RouteValueDictionary(), values);

            return(binder.BindValues(result.AcceptedValues));
        }
示例#10
0
        public void GetVirtualPathWithMultiSegmentWithOptionalParam(
            string template,
            RouteValueDictionary defaults,
            RouteValueDictionary ambientValues,
            RouteValueDictionary values,
            string expected)
        {
            // Arrange
            var encoder = new UrlTestEncoder();
            var binder  = new TemplateBinder(
                encoder,
                new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy(encoder)),
                TemplateParser.Parse(template),
                defaults);

            // Act & Assert
            var result = binder.GetValues(ambientValues: ambientValues, values: values);

            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);

            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }
        public void Binding_WithEmptyAndNull_DefaultValues(
            string template,
            RouteValueDictionary defaults,
            RouteValueDictionary values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(
                UrlEncoder.Default,
                new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
                TemplateParser.Parse(template),
                defaults);

            // Act & Assert
            var result = binder.GetValues(ambientValues: null, values: values);

            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);

            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }
示例#12
0
        public void GetVirtualPathWithMultiSegmentWithOptionalParam(
            string template,
            IReadOnlyDictionary <string, object> defaults,
            IDictionary <string, object> ambientValues,
            IDictionary <string, object> values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(
                TemplateParser.Parse(template),
                defaults);

            // Act & Assert
            var result = binder.GetValues(ambientValues: ambientValues, values: values);

            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);

            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }
示例#13
0
        public void Binding_WithEmptyAndNull_DefaultValues(
            string template,
            IReadOnlyDictionary <string, object> defaults,
            IDictionary <string, object> values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(
                TemplateParser.Parse(template),
                defaults);

            // Act & Assert
            var result = binder.GetValues(ambientValues: null, values: values);

            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);

            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }
示例#14
0
        public void TemplateBinder_KeepsExplicitlySuppliedRouteValues_OnFailedRouetMatch()
        {
            // Arrange
            var template = "{area?}/{controller=Home}/{action=Index}/{id?}";
            var binder   = new TemplateBinder(BinderFactory.Create(template));

            var ambientValues = new RouteValueDictionary();
            var routeValues   = new RouteValueDictionary(new { controller = "Test", action = "Index" });

            // Act
            var templateValuesResult = binder.GetValues(ambientValues, routeValues);
            var boundTemplate        = binder.BindValues(templateValuesResult.AcceptedValues);

            // Assert
            Assert.Null(boundTemplate);
            Assert.Equal(2, templateValuesResult.CombinedValues.Count);
            object routeValue;

            Assert.True(templateValuesResult.CombinedValues.TryGetValue("controller", out routeValue));
            Assert.Equal("Test", routeValue?.ToString());
            Assert.True(templateValuesResult.CombinedValues.TryGetValue("action", out routeValue));
            Assert.Equal("Index", routeValue?.ToString());
        }
示例#15
0
        public void GetVirtualPathWithMultiSegmentWithOptionalParam(
            string template,
            RouteValueDictionary defaults,
            RouteValueDictionary ambientValues,
            RouteValueDictionary values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(BinderFactory.Create(template, defaults));

            // Act & Assert
            var result = binder.GetValues(ambientValues: ambientValues, values: values);

            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);

            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }
示例#16
0
        private static bool TryProcessPrefixTemplate(HttpRequest request, RoutePattern routePattern, out string path)
        {
            // TODO: Do you have a better way to process the prefix template?
            Contract.Assert(request != null);
            Contract.Assert(routePattern != null);

            HttpContext           httpContext    = request.HttpContext;
            TemplateBinderFactory factory        = request.HttpContext.RequestServices.GetRequiredService <TemplateBinderFactory>();
            TemplateBinder        templateBinder = factory.Create(routePattern);

            RouteValueDictionary ambientValues = GetAmbientValues(httpContext);

            var templateValuesResult = templateBinder.GetValues(ambientValues, request.RouteValues);

            if (templateValuesResult == null)
            {
                // We're missing one of the required values for this route.
                path = default;
                return(false);
            }

            if (!templateBinder.TryProcessConstraints(httpContext, templateValuesResult.CombinedValues, out var _, out var _))
            {
                path = default;
                return(false);
            }

            path = templateBinder.BindValues(templateValuesResult.AcceptedValues);
            int index = path.IndexOf("?", StringComparison.Ordinal); // remove the query string

            if (index >= 0)
            {
                path = path.Substring(0, index);
            }

            return(true);
        }
示例#17
0
        public void Binding_WithEmptyAndNull_DefaultValues(
            string template,
            RouteValueDictionary defaults,
            RouteValueDictionary values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(BinderFactory.Create(template, defaults));

            // Act & Assert
            var result = binder.GetValues(ambientValues: null, values: values);

            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);

            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }
        private string GetLink(
            RouteTemplate template,
            RouteValueDictionary defaults,
            RouteValueDictionary explicitValues,
            RouteValueDictionary ambientValues)
        {
            var templateBinder = new TemplateBinder(
                UrlEncoder.Default,
                _uriBuildingContextPool,
                template,
                defaults);

            var values = templateBinder.GetValues(ambientValues, explicitValues);

            if (values == null)
            {
                // We're missing one of the required values for this route.
                return(null);
            }

            //TODO: route constraint matching here

            return(templateBinder.BindValues(values.AcceptedValues));
        }
示例#19
0
        public void GetVirtualPathWithMultiSegmentWithOptionalParam(
            string template,
            IReadOnlyDictionary<string, object> defaults,
            IDictionary<string, object> ambientValues,
            IDictionary<string, object> values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(
                TemplateParser.Parse(template),
                defaults);

            // Act & Assert
            var result = binder.GetValues(ambientValues: ambientValues, values: values);
            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);
            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }
示例#20
0
        public void Binding_WithEmptyAndNull_DefaultValues(
            string template,
            IReadOnlyDictionary<string, object> defaults,
            IDictionary<string, object> values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(
                TemplateParser.Parse(template),
                defaults);

            // Act & Assert
            var result = binder.GetValues(ambientValues: null, values: values);
            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);
            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }
示例#21
0
        private static void RunTest(
            string template,
            IReadOnlyDictionary<string, object> defaults,
            IDictionary<string, object> ambientValues,
            IDictionary<string, object> values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(TemplateParser.Parse(template), defaults);

            // Act & Assert
            var result = binder.GetValues(ambientValues, values);
            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);
            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);

                // We want to chop off the query string and compare that using an unordered comparison
                var expectedParts = new PathAndQuery(expected);
                var actualParts = new PathAndQuery(boundTemplate);

                Assert.Equal(expectedParts.Path, actualParts.Path);

                if (expectedParts.Parameters == null)
                {
                    Assert.Null(actualParts.Parameters);
                }
                else
                {
                    Assert.Equal(expectedParts.Parameters.Count, actualParts.Parameters.Count);

                    foreach (var kvp in expectedParts.Parameters)
                    {
                        string value;
                        Assert.True(actualParts.Parameters.TryGetValue(kvp.Key, out value));
                        Assert.Equal(kvp.Value, value);
                    }
                }
            }
        }
示例#22
0
        private static void RunTest(
            string template,
            RouteValueDictionary defaults,
            RouteValueDictionary ambientValues,
            RouteValueDictionary values,
            string expected,
            UrlEncoder encoder = null)
        {
            // Arrange
            encoder = encoder ?? new UrlTestEncoder();

            var binder = new TemplateBinder(
                encoder,
                new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy(encoder)),
                TemplateParser.Parse(template),
                defaults);

            // Act & Assert
            var result = binder.GetValues(ambientValues, values);
            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);
            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);

                // We want to chop off the query string and compare that using an unordered comparison
                var expectedParts = new PathAndQuery(expected);
                var actualParts = new PathAndQuery(boundTemplate);

                Assert.Equal(expectedParts.Path, actualParts.Path);

                if (expectedParts.Parameters == null)
                {
                    Assert.Null(actualParts.Parameters);
                }
                else
                {
                    Assert.Equal(expectedParts.Parameters.Count, actualParts.Parameters.Count);

                    foreach (var kvp in expectedParts.Parameters)
                    {
                        string value;
                        Assert.True(actualParts.Parameters.TryGetValue(kvp.Key, out value));
                        Assert.Equal(kvp.Value, value);
                    }
                }
            }
        }
示例#23
0
        public void GetVirtualPathWithMultiSegmentWithOptionalParam(
            string template,
            RouteValueDictionary defaults,
            RouteValueDictionary ambientValues,
            RouteValueDictionary values,
            string expected)
        {
            // Arrange
            var encoder = new UrlTestEncoder();
            var binder = new TemplateBinder(
                encoder,
                new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy(encoder)),
                TemplateParser.Parse(template),
                defaults);

            // Act & Assert
            var result = binder.GetValues(ambientValues: ambientValues, values: values);
            if (result == null)
            {
                if (expected == null)
                {
                    return;
                }
                else
                {
                    Assert.NotNull(result);
                }
            }

            var boundTemplate = binder.BindValues(result.AcceptedValues);
            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }
        private VirtualPathData GenerateVirtualPath(
            VirtualPathContext context,
            OutboundRouteEntry entry,
            TemplateBinder binder)
        {
            // In attribute the context includes the values that are used to select this entry - typically
            // these will be the standard 'action', 'controller' and maybe 'area' tokens. However, we don't
            // want to pass these to the link generation code, or else they will end up as query parameters.
            //
            // So, we need to exclude from here any values that are 'required link values', but aren't
            // parameters in the template.
            //
            // Ex:
            //      template: api/Products/{action}
            //      required values: { id = "5", action = "Buy", Controller = "CoolProducts" }
            //
            //      result: { id = "5", action = "Buy" }
            var inputValues = new RouteValueDictionary();

            foreach (var kvp in context.Values)
            {
                if (entry.RequiredLinkValues.ContainsKey(kvp.Key))
                {
                    var parameter = entry.RouteTemplate.GetParameter(kvp.Key);

                    if (parameter == null)
                    {
                        continue;
                    }
                }

                inputValues.Add(kvp.Key, kvp.Value);
            }

            var bindingResult = binder.GetValues(context.AmbientValues, inputValues);

            if (bindingResult == null)
            {
                // A required parameter in the template didn't get a value.
                return(null);
            }

            var matched = RouteConstraintMatcher.Match(
                entry.Constraints,
                bindingResult.CombinedValues,
                context.HttpContext,
                this,
                RouteDirection.UrlGeneration,
                _constraintLogger);

            if (!matched)
            {
                // A constraint rejected this link.
                return(null);
            }

            var pathData = entry.Handler.GetVirtualPath(context);

            if (pathData != null)
            {
                // If path is non-null then the target router short-circuited, we don't expect this
                // in typical MVC scenarios.
                return(pathData);
            }

            var path = binder.BindValues(bindingResult.AcceptedValues);

            if (path == null)
            {
                return(null);
            }

            return(new VirtualPathData(this, path));
        }