diff --git a/samples/RoutingSample.Web/DelegateRouteEndpoint.cs b/samples/RoutingSample.Web/DelegateRouteEndpoint.cs index ee27c25a11..904404e1b4 100644 --- a/samples/RoutingSample.Web/DelegateRouteEndpoint.cs +++ b/samples/RoutingSample.Web/DelegateRouteEndpoint.cs @@ -25,8 +25,6 @@ namespace RoutingSample.Web public VirtualPathData GetVirtualPath(VirtualPathContext context) { - // We don't really care what the values look like. - context.IsBound = true; return null; } } diff --git a/src/Microsoft.AspNet.Routing.Abstractions/VirtualPathContext.cs b/src/Microsoft.AspNet.Routing.Abstractions/VirtualPathContext.cs index ac67152c52..deb7b1b97d 100644 --- a/src/Microsoft.AspNet.Routing.Abstractions/VirtualPathContext.cs +++ b/src/Microsoft.AspNet.Routing.Abstractions/VirtualPathContext.cs @@ -1,13 +1,21 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Collections.Generic; using Microsoft.AspNet.Http; namespace Microsoft.AspNet.Routing { + /// + /// A context for virtual path generation operations. + /// public class VirtualPathContext { + /// + /// Creates a new . + /// + /// The associated with the current request. + /// The set of route values associated with the current request. + /// The set of new values provided for virtual path generation. public VirtualPathContext( HttpContext httpContext, RouteValueDictionary ambientValues, @@ -16,28 +24,43 @@ namespace Microsoft.AspNet.Routing { } + /// + /// Creates a new . + /// + /// The associated with the current request. + /// The set of route values associated with the current request. + /// The set of new values provided for virtual path generation. + /// The name of the route to use for virtual path generation. public VirtualPathContext( - HttpContext context, + HttpContext httpContext, RouteValueDictionary ambientValues, RouteValueDictionary values, string routeName) { - Context = context; + HttpContext = httpContext; AmbientValues = ambientValues; Values = values; RouteName = routeName; } - public string RouteName { get; } - - public IDictionary ProvidedValues { get; set; } - + /// + /// Gets the set of route values associated with the current request. + /// public RouteValueDictionary AmbientValues { get; } - public HttpContext Context { get; } + /// + /// Gets the associated with the current request. + /// + public HttpContext HttpContext { get; } - public bool IsBound { get; set; } + /// + /// Gets the name of the route to use for virtual path generation. + /// + public string RouteName { get; } + /// + /// Gets the set of new values provided for virtual path generation. + /// public RouteValueDictionary Values { get; } } } diff --git a/src/Microsoft.AspNet.Routing/RouteCollection.cs b/src/Microsoft.AspNet.Routing/RouteCollection.cs index 1398e8cace..d5851a99db 100644 --- a/src/Microsoft.AspNet.Routing/RouteCollection.cs +++ b/src/Microsoft.AspNet.Routing/RouteCollection.cs @@ -86,123 +86,58 @@ namespace Microsoft.AspNet.Routing public virtual VirtualPathData GetVirtualPath(VirtualPathContext context) { - EnsureOptions(context.Context); - - // If we're using Best-Effort link generation then it means that we'll first look for a route where - // the route values are validated (context.IsBound == true). If we can't find a match like that, then - // we'll return the path from the first route to return one. - var useBestEffort = _options.UseBestEffortLinkGeneration; + EnsureOptions(context.HttpContext); if (!string.IsNullOrEmpty(context.RouteName)) { - var isValidated = false; - VirtualPathData bestPathData = null; + VirtualPathData namedRoutePathData = null; INamedRouter matchedNamedRoute; if (_namedRoutes.TryGetValue(context.RouteName, out matchedNamedRoute)) { - bestPathData = matchedNamedRoute.GetVirtualPath(context); - isValidated = context.IsBound; + namedRoutePathData = matchedNamedRoute.GetVirtualPath(context); } - // If we get here and context.IsBound == true, then we know we have a match, we want to keep - // iterating to see if we have multiple matches. - foreach (var unnamedRoute in _unnamedRoutes) + var pathData = GetVirtualPath(context, _unnamedRoutes); + + // If the named route and one of the unnamed routes also matches, then we have an ambiguity. + if (namedRoutePathData != null && pathData != null) { - // reset because we're sharing the context - context.IsBound = false; - - var pathData = unnamedRoute.GetVirtualPath(context); - if (pathData == null) - { - continue; - } - - if (bestPathData != null) - { - // There was already a previous route which matched the name. - throw new InvalidOperationException( - Resources.FormatNamedRoutes_AmbiguousRoutesFound(context.RouteName)); - } - else if (context.IsBound) - { - // This is the first 'validated' match that we've found. - bestPathData = pathData; - isValidated = true; - } - else - { - Debug.Assert(bestPathData == null); - - // This is the first 'unvalidated' match that we've found. - bestPathData = pathData; - isValidated = false; - } + var message = Resources.FormatNamedRoutes_AmbiguousRoutesFound(context.RouteName); + throw new InvalidOperationException(message); } - if (isValidated || useBestEffort) - { - context.IsBound = isValidated; - - if (bestPathData != null) - { - bestPathData = new VirtualPathData( - bestPathData.Router, - NormalizeVirtualPath(bestPathData.VirtualPath), - bestPathData.DataTokens); - } - - return bestPathData; - } - else - { - return null; - } + return NormalizeVirtualPath(namedRoutePathData ?? pathData); } else { - VirtualPathData bestPathData = null; - for (var i = 0; i < Count; i++) - { - var route = this[i]; - - var pathData = route.GetVirtualPath(context); - if (pathData == null) - { - continue; - } - - if (context.IsBound) - { - // This route has validated route values, short circuit. - return new VirtualPathData( - pathData.Router, - NormalizeVirtualPath(pathData.VirtualPath), - pathData.DataTokens); - } - else if (bestPathData == null) - { - // The values aren't validated, but this is the best we've seen so far - bestPathData = pathData; - } - } - - if (useBestEffort) - { - return new VirtualPathData( - bestPathData.Router, - NormalizeVirtualPath(bestPathData.VirtualPath), - bestPathData.DataTokens); - } - else - { - return null; - } + return NormalizeVirtualPath(GetVirtualPath(context, _routes)); } } - private PathString NormalizeVirtualPath(PathString path) + private VirtualPathData GetVirtualPath(VirtualPathContext context, List routes) { - var url = path.Value; + for (var i = 0; i < routes.Count; i++) + { + var route = routes[i]; + + var pathData = route.GetVirtualPath(context); + if (pathData != null) + { + return pathData; + } + } + + return null; + } + + private VirtualPathData NormalizeVirtualPath(VirtualPathData pathData) + { + if (pathData == null) + { + return pathData; + } + + var url = pathData.VirtualPath.Value; if (!string.IsNullOrEmpty(url) && (_options.LowercaseUrls || _options.AppendTrailingSlash)) { @@ -229,10 +164,10 @@ namespace Microsoft.AspNet.Routing // queryString will contain the delimiter ? or # as the first character, so it's safe to append. url = urlWithoutQueryString + queryString; - return new PathString(url); + return new VirtualPathData(pathData.Router, url, pathData.DataTokens); } - return path; + return pathData; } private void EnsureOptions(HttpContext context) diff --git a/src/Microsoft.AspNet.Routing/RouteOptions.cs b/src/Microsoft.AspNet.Routing/RouteOptions.cs index aadb80a450..894b3eb249 100644 --- a/src/Microsoft.AspNet.Routing/RouteOptions.cs +++ b/src/Microsoft.AspNet.Routing/RouteOptions.cs @@ -69,13 +69,5 @@ namespace Microsoft.AspNet.Routing {"required", typeof(RequiredRouteConstraint) }, }; } - - /// - /// Gets or sets the value that enables best-effort link generation. - /// - /// If enabled, link generation will use allow link generation to succeed when the set of values provided - /// cannot be validated. - /// - public bool UseBestEffortLinkGeneration { get; set; } } } diff --git a/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs b/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs index e3f9538ccd..e835d4dceb 100644 --- a/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs +++ b/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs @@ -179,21 +179,19 @@ namespace Microsoft.AspNet.Routing.Template return null; } - EnsureLoggers(context.Context); - if (!RouteConstraintMatcher.Match(Constraints, - values.CombinedValues, - context.Context, - this, - RouteDirection.UrlGeneration, - _constraintLogger)) + EnsureLoggers(context.HttpContext); + if (!RouteConstraintMatcher.Match( + Constraints, + values.CombinedValues, + context.HttpContext, + this, + RouteDirection.UrlGeneration, + _constraintLogger)) { return null; } - // Validate that the target can accept these values. - var childContext = CreateChildVirtualPathContext(context, values.AcceptedValues); - - var pathData = _target.GetVirtualPath(childContext); + var pathData = _target.GetVirtualPath(context); if (pathData != null) { // If the target generates a value then that can short circuit. @@ -219,43 +217,9 @@ namespace Microsoft.AspNet.Routing.Template } } - context.IsBound = childContext.IsBound; - return pathData; } - private VirtualPathContext CreateChildVirtualPathContext( - VirtualPathContext context, - IDictionary acceptedValues) - { - // We want to build the set of values that would be provided if this route were to generated - // a link and then immediately match it. This includes all the accepted parameter values, and - // the defaults. Accepted values that would go in the query string aren't included. - var providedValues = new RouteValueDictionary(); - - foreach (var parameter in _parsedTemplate.Parameters) - { - object value; - if (acceptedValues.TryGetValue(parameter.Name, out value)) - { - providedValues.Add(parameter.Name, value); - } - } - - foreach (var kvp in _defaults) - { - if (!providedValues.ContainsKey(kvp.Key)) - { - providedValues.Add(kvp.Key, kvp.Value); - } - } - - return new VirtualPathContext(context.Context, context.AmbientValues, context.Values) - { - ProvidedValues = providedValues, - }; - } - private static IReadOnlyDictionary GetConstraints( IInlineConstraintResolver inlineConstraintResolver, string template, diff --git a/src/Microsoft.AspNet.Routing/Tree/TreeRouter.cs b/src/Microsoft.AspNet.Routing/Tree/TreeRouter.cs index 7aa8a65333..ec48e11312 100644 --- a/src/Microsoft.AspNet.Routing/Tree/TreeRouter.cs +++ b/src/Microsoft.AspNet.Routing/Tree/TreeRouter.cs @@ -143,7 +143,6 @@ namespace Microsoft.AspNet.Routing.Tree var path = GenerateVirtualPath(context, match.Entry); if (path != null) { - context.IsBound = true; return path; } } @@ -382,7 +381,6 @@ namespace Microsoft.AspNet.Routing.Tree var path = GenerateVirtualPath(context, entry); if (path != null) { - context.IsBound = true; return path; } } @@ -430,7 +428,7 @@ namespace Microsoft.AspNet.Routing.Tree var matched = RouteConstraintMatcher.Match( entry.Constraints, bindingResult.CombinedValues, - context.Context, + context.HttpContext, this, RouteDirection.UrlGeneration, _constraintLogger); @@ -441,30 +439,13 @@ namespace Microsoft.AspNet.Routing.Tree return null; } - // These values are used to signal to the next route what we would produce if we round-tripped - // (generate a link and then parse). In MVC the 'next route' is typically the MvcRouteHandler. - var providedValues = new Dictionary( - bindingResult.AcceptedValues, - StringComparer.OrdinalIgnoreCase); - providedValues.Add(RouteGroupKey, entry.RouteGroup); - - var childContext = new VirtualPathContext(context.Context, context.AmbientValues, context.Values) - { - ProvidedValues = providedValues, - }; - - var pathData = _next.GetVirtualPath(childContext); + var pathData = _next.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; } - else if (!childContext.IsBound) - { - // The target router has rejected these values. We don't expect this in typical MVC scenarios. - return null; - } var path = entry.Binder.BindValues(bindingResult.AcceptedValues); if (path == null) diff --git a/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs b/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs index 796e441857..e3ca1b34fa 100644 --- a/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs +++ b/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs @@ -47,7 +47,6 @@ namespace Microsoft.AspNet.Routing var virtualPathContext = CreateVirtualPathContext( options: GetRouteOptions( lowerCaseUrls: lowercaseUrls, - useBestEffortLinkGeneration: true, appendTrailingSlash: appendTrailingSlash)); // Act @@ -88,7 +87,7 @@ namespace Microsoft.AspNet.Routing } [Theory] - [MemberData("DataTokensTestData")] + [MemberData(nameof(DataTokensTestData))] public void GetVirtualPath_ReturnsDataTokens(RouteValueDictionary dataTokens, string routerName) { // Arrange @@ -286,306 +285,13 @@ namespace Microsoft.AspNet.Routing innerRouteCollection.Add(namedRoute); routeCollection.Add(innerRouteCollection); - var options = new RouteOptions() - { - UseBestEffortLinkGeneration = true, - }; - - var virtualPathContext = CreateVirtualPathContext("Ambiguous", options: options); + var virtualPathContext = CreateVirtualPathContext("Ambiguous", options: new RouteOptions()); // Act & Assert var ex = Assert.Throws(() => routeCollection.GetVirtualPath(virtualPathContext)); Assert.Equal("The supplied route name 'Ambiguous' is ambiguous and matched more than one route.", ex.Message); } - [Fact] - public void GetVirtualPath_NamedRoute_BestEffort_BestInTopCollection() - { - // Arrange - var bestMatch = CreateNamedRoute("Match", accept: true, matchValue: "best"); - var noMatch = CreateNamedRoute("NoMatch", accept: true, matchValue: "bad"); - - var routeCollection = new RouteCollection(); - routeCollection.Add(bestMatch); - - var innerRouteCollection = new RouteCollection(); - innerRouteCollection.Add(noMatch); - routeCollection.Add(innerRouteCollection); - - var options = new RouteOptions() - { - UseBestEffortLinkGeneration = true, - }; - - var virtualPathContext = CreateVirtualPathContext("Match", options: options); - - // Act - var pathData = routeCollection.GetVirtualPath(virtualPathContext); - - Assert.Equal(new PathString("/best"), pathData.VirtualPath); - var namedRouter = Assert.IsAssignableFrom(pathData.Router); - Assert.Equal("Match", namedRouter.Name); - Assert.Empty(pathData.DataTokens); - } - - [Fact] - public void GetVirtualPath_NamedRoute_BestEffort_BestMatchInNestedCollection() - { - // Arrange - var bestMatch = CreateNamedRoute("NoMatch", accept: true, matchValue: "bad"); - var noMatch = CreateNamedRoute("Match", accept: true, matchValue: "best"); - - var routeCollection = new RouteCollection(); - routeCollection.Add(noMatch); - - var innerRouteCollection = new RouteCollection(); - innerRouteCollection.Add(bestMatch); - routeCollection.Add(innerRouteCollection); - - var options = new RouteOptions() - { - UseBestEffortLinkGeneration = true, - }; - - var virtualPathContext = CreateVirtualPathContext("Match", options: options); - - // Act - var pathData = routeCollection.GetVirtualPath(virtualPathContext); - - Assert.Equal(new PathString("/best"), pathData.VirtualPath); - var namedRouter = Assert.IsAssignableFrom(pathData.Router); - Assert.Equal("Match", namedRouter.Name); - Assert.Empty(pathData.DataTokens); - } - - [Fact] - public void GetVirtualPath_NamedRoute_BestEffort_FirstRouteWins() - { - // Arrange - var bestMatch = CreateNamedRoute("Match", accept: false, matchValue: "best"); - var noMatch = CreateNamedRoute("NoMatch", accept: false, matchValue: "bad"); - - var routeCollection = new RouteCollection(); - routeCollection.Add(noMatch); - - var innerRouteCollection = new RouteCollection(); - innerRouteCollection.Add(bestMatch); - routeCollection.Add(innerRouteCollection); - - var options = new RouteOptions() - { - UseBestEffortLinkGeneration = true, - }; - - var virtualPathContext = CreateVirtualPathContext("Match", options: options); - - // Act - var pathData = routeCollection.GetVirtualPath(virtualPathContext); - - Assert.Equal(new PathString("/best"), pathData.VirtualPath); - var namedRouter = Assert.IsAssignableFrom(pathData.Router); - Assert.Equal("Match", namedRouter.Name); - Assert.Empty(pathData.DataTokens); - } - - [Fact] - public void GetVirtualPath_BestEffort_FirstRouteWins() - { - // Arrange - var route1 = CreateRoute(accept: false, match: true, matchValue: "best"); - var route2 = CreateRoute(accept: false, match: true, matchValue: "bad"); - var route3 = CreateRoute(accept: false, match: true, matchValue: "bad"); - - var routeCollection = new RouteCollection(); - routeCollection.Add(route1.Object); - routeCollection.Add(route2.Object); - routeCollection.Add(route3.Object); - - var options = new RouteOptions() - { - UseBestEffortLinkGeneration = true, - }; - - var virtualPathContext = CreateVirtualPathContext(options: options); - - // Act - var pathData = routeCollection.GetVirtualPath(virtualPathContext); - - Assert.Equal(new PathString("/best"), pathData.VirtualPath); - Assert.Same(route1.Object, pathData.Router); - Assert.Empty(pathData.DataTokens); - - // All of these should be called - route1.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route2.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route3.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - } - - [Fact] - public void GetVirtualPath_NoBestEffort_NoMatch() - { - // Arrange - var route1 = CreateRoute(accept: false, match: true, matchValue: "best"); - var route2 = CreateRoute(accept: false, match: true, matchValue: "bad"); - var route3 = CreateRoute(accept: false, match: true, matchValue: "bad"); - - var routeCollection = new RouteCollection(); - routeCollection.Add(route1.Object); - routeCollection.Add(route2.Object); - routeCollection.Add(route3.Object); - - var options = new RouteOptions() - { - UseBestEffortLinkGeneration = false, - }; - - var virtualPathContext = CreateVirtualPathContext(options: options); - - // Act - var path = routeCollection.GetVirtualPath(virtualPathContext); - - Assert.Null(path); - - // All of these should be called - route1.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route2.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route3.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - } - - [Fact] - public void GetVirtualPath_BestEffort_FirstRouteWins_WithNonMatchingRoutes() - { - // Arrange - var route1 = CreateRoute(accept: false, match: false, matchValue: "bad"); - var route2 = CreateRoute(accept: false, match: true, matchValue: "best"); - var route3 = CreateRoute(accept: false, match: true, matchValue: "bad"); - - var routeCollection = new RouteCollection(); - routeCollection.Add(route1.Object); - routeCollection.Add(route2.Object); - routeCollection.Add(route3.Object); - - var options = new RouteOptions() - { - UseBestEffortLinkGeneration = true, - }; - - var virtualPathContext = CreateVirtualPathContext(options: options); - - // Act - var pathData = routeCollection.GetVirtualPath(virtualPathContext); - - Assert.Equal(new PathString("/best"), pathData.VirtualPath); - Assert.Same(route2.Object, pathData.Router); - Assert.Empty(pathData.DataTokens); - - // All of these should be called - route1.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route2.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route3.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - } - - [Fact] - public void GetVirtualPath_BestEffort_FirstValidatedValuesWins() - { - // Arrange - var route1 = CreateRoute(accept: false, match: true, matchValue: "bad"); - var route2 = CreateRoute(accept: false, match: true, matchValue: "bad"); - var route3 = CreateRoute(accept: true, match: true, matchValue: "best"); - - var routeCollection = new RouteCollection(); - routeCollection.Add(route1.Object); - routeCollection.Add(route2.Object); - routeCollection.Add(route3.Object); - - var options = new RouteOptions() - { - UseBestEffortLinkGeneration = true, - }; - - var virtualPathContext = CreateVirtualPathContext(options: options); - - // Act - var pathData = routeCollection.GetVirtualPath(virtualPathContext); - - Assert.Equal(new PathString("/best"), pathData.VirtualPath); - Assert.Same(route3.Object, pathData.Router); - Assert.Empty(pathData.DataTokens); - - // All of these should be called - route1.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route2.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route3.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - } - - [Fact] - public void GetVirtualPath_BestEffort_FirstValidatedValuesWins_ShortCircuit() - { - // Arrange - var route1 = CreateRoute(accept: false, match: true, matchValue: "bad"); - var route2 = CreateRoute(accept: true, match: true, matchValue: "best"); - var route3 = CreateRoute(accept: true, match: true, matchValue: "bad"); - - var routeCollection = new RouteCollection(); - routeCollection.Add(route1.Object); - routeCollection.Add(route2.Object); - routeCollection.Add(route3.Object); - - var options = new RouteOptions() - { - UseBestEffortLinkGeneration = true, - }; - - var virtualPathContext = CreateVirtualPathContext(options: options); - - // Act - var pathData = routeCollection.GetVirtualPath(virtualPathContext); - - Assert.Equal(new PathString("/best"), pathData.VirtualPath); - Assert.Same(route2.Object, pathData.Router); - Assert.Empty(pathData.DataTokens); - - route1.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route2.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route3.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Never()); - } - - [Fact] - public void GetVirtualPath_BestEffort_FirstValidatedValuesWins_Nested() - { - // Arrange - var route1 = CreateRoute(accept: false, match: true, matchValue: "bad"); - var route2 = CreateRoute(accept: false, match: true, matchValue: "bad"); - var route3 = CreateRoute(accept: true, match: true, matchValue: "best"); - - var routeCollection = new RouteCollection(); - routeCollection.Add(route1.Object); - - var innerRouteCollection = new RouteCollection(); - innerRouteCollection.Add(route2.Object); - innerRouteCollection.Add(route3.Object); - routeCollection.Add(innerRouteCollection); - - var options = new RouteOptions() - { - UseBestEffortLinkGeneration = true, - }; - - var virtualPathContext = CreateVirtualPathContext(options: options); - - // Act - var pathData = routeCollection.GetVirtualPath(virtualPathContext); - - Assert.Equal(new PathString("/best"), pathData.VirtualPath); - Assert.Same(route3.Object, pathData.Router); - Assert.Empty(pathData.DataTokens); - - // All of these should be called - route1.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route2.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - route3.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); - } - // "Integration" tests for RouteCollection public static IEnumerable IntegrationTestData @@ -633,7 +339,7 @@ namespace Microsoft.AspNet.Routing } [Theory] - [MemberData("IntegrationTestData")] + [MemberData(nameof(IntegrationTestData))] public void GetVirtualPath_Success( string template, RouteValueDictionary values, @@ -651,12 +357,37 @@ namespace Microsoft.AspNet.Routing var pathData = routeCollection.GetVirtualPath(context); // Assert - Assert.True(context.IsBound); Assert.Equal(new PathString(expectedUrl), pathData.VirtualPath); Assert.Same(route, pathData.Router); Assert.Empty(pathData.DataTokens); } + [Fact] + public void GetVirtualPath_NoBestEffort_NoMatch() + { + // Arrange + var route1 = CreateRoute(accept: false, match: false, matchValue: "bad"); + var route2 = CreateRoute(accept: false, match: false, matchValue: "bad"); + var route3 = CreateRoute(accept: false, match: false, matchValue: "bad"); + + var routeCollection = new RouteCollection(); + routeCollection.Add(route1.Object); + routeCollection.Add(route2.Object); + routeCollection.Add(route3.Object); + + var virtualPathContext = CreateVirtualPathContext(); + + // Act + var path = routeCollection.GetVirtualPath(virtualPathContext); + + Assert.Null(path); + + // All of these should be called + route1.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); + route2.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); + route3.Verify(r => r.GetVirtualPath(It.IsAny()), Times.Once()); + } + // DataTokens test data for RouterCollection.GetVirtualPath public static IEnumerable DataTokensTestData { @@ -744,7 +475,6 @@ namespace Microsoft.AspNet.Routing var target = new Mock(MockBehavior.Strict); target .Setup(e => e.GetVirtualPath(It.IsAny())) - .Callback(c => c.IsBound = accept && c.RouteName == name) .Returns(c => c.RouteName == name ? new VirtualPathData(target.Object, matchValue) : null) .Verifiable(); @@ -770,7 +500,6 @@ namespace Microsoft.AspNet.Routing var target = new Mock(MockBehavior.Strict); target .Setup(e => e.GetVirtualPath(It.IsAny())) - .Callback(c => c.IsBound = true) .Returns(rc => null); var resolverMock = new Mock(); @@ -876,7 +605,6 @@ namespace Microsoft.AspNet.Routing var target = new Mock(MockBehavior.Strict); target .Setup(e => e.GetVirtualPath(It.IsAny())) - .Callback(c => c.IsBound = accept) .Returns(accept || match ? new VirtualPathData(target.Object, matchValue) : null) .Verifiable(); @@ -891,12 +619,10 @@ namespace Microsoft.AspNet.Routing private static RouteOptions GetRouteOptions( bool lowerCaseUrls = false, - bool useBestEffortLinkGeneration = true, bool appendTrailingSlash = false) { var routeOptions = new RouteOptions(); routeOptions.LowercaseUrls = lowerCaseUrls; - routeOptions.UseBestEffortLinkGeneration = useBestEffortLinkGeneration; routeOptions.AppendTrailingSlash = appendTrailingSlash; return routeOptions; diff --git a/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTest.cs b/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTest.cs index 323a1a77ca..5775c61f3e 100644 --- a/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTest.cs +++ b/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTest.cs @@ -881,7 +881,6 @@ namespace Microsoft.AspNet.Routing.Template var pathData = route.GetVirtualPath(context); // Assert - Assert.True(context.IsBound); Assert.Equal(new PathString("/Home"), pathData.VirtualPath); Assert.Same(route, pathData.Router); Assert.Empty(pathData.DataTokens); @@ -898,7 +897,6 @@ namespace Microsoft.AspNet.Routing.Template var path = route.GetVirtualPath(context); // Assert - Assert.False(context.IsBound); Assert.Null(path); } @@ -913,7 +911,6 @@ namespace Microsoft.AspNet.Routing.Template var target = new Mock(MockBehavior.Strict); target .Setup(r => r.GetVirtualPath(It.IsAny())) - .Callback(c => c.IsBound = true) .Returns(() => new VirtualPathData(target.Object, path, dataTokens)); var routeDataTokens = @@ -958,7 +955,6 @@ namespace Microsoft.AspNet.Routing.Template var target = new Mock(MockBehavior.Strict); target .Setup(r => r.GetVirtualPath(It.IsAny())) - .Callback(c => c.IsBound = true) .Returns(() => null); var route = CreateRoute( @@ -998,7 +994,6 @@ namespace Microsoft.AspNet.Routing.Template var pathData = route.GetVirtualPath(context); // Assert - Assert.False(context.IsBound); Assert.Equal(new PathString("/Home"), pathData.VirtualPath); Assert.Same(route, pathData.Router); Assert.Empty(pathData.DataTokens); @@ -1015,7 +1010,6 @@ namespace Microsoft.AspNet.Routing.Template var pathData = route.GetVirtualPath(context); // Assert - Assert.True(context.IsBound); Assert.Equal(new PathString("/Home/Index"), pathData.VirtualPath); Assert.Same(route, pathData.Router); Assert.Empty(pathData.DataTokens); @@ -1037,7 +1031,6 @@ namespace Microsoft.AspNet.Routing.Template var virtualPath = r.GetVirtualPath(context); // Assert - Assert.False(context.IsBound); Assert.Null(virtualPath); } @@ -1057,7 +1050,6 @@ namespace Microsoft.AspNet.Routing.Template var pathData = route.GetVirtualPath(context); // Assert - Assert.True(context.IsBound); Assert.NotNull(pathData); Assert.Equal(new PathString("/hello/1234"), pathData.VirtualPath); Assert.Same(route, pathData.Router); @@ -1080,7 +1072,6 @@ namespace Microsoft.AspNet.Routing.Template var virtualPath = r.GetVirtualPath(context); // Assert - Assert.False(context.IsBound); Assert.Null(virtualPath); } @@ -1100,7 +1091,6 @@ namespace Microsoft.AspNet.Routing.Template var pathData = route.GetVirtualPath(context); // Assert - Assert.True(context.IsBound); Assert.NotNull(pathData); Assert.Equal(new PathString("/hello/1234"), pathData.VirtualPath); Assert.Same(route, pathData.Router); @@ -1132,7 +1122,6 @@ namespace Microsoft.AspNet.Routing.Template var pathData = route.GetVirtualPath(context); // Assert - Assert.True(context.IsBound); Assert.NotNull(pathData); Assert.Equal(new PathString("/hello/1234"), pathData.VirtualPath); Assert.Same(route, pathData.Router); @@ -1141,92 +1130,6 @@ namespace Microsoft.AspNet.Routing.Template target.VerifyAll(); } - [Fact] - public void GetVirtualPath_Sends_ProvidedValues() - { - // Arrange - VirtualPathContext childContext = null; - var target = new Mock(MockBehavior.Strict); - target - .Setup(r => r.GetVirtualPath(It.IsAny())) - .Callback(c => { childContext = c; c.IsBound = true; }) - .Returns(null); - - var route = CreateRoute(target.Object, "{controller}/{action}"); - var context = CreateVirtualPathContext( - new { action = "Store" }, - new { Controller = "Home", action = "Blog" }); - - var expectedValues = new RouteValueDictionary(new { controller = "Home", action = "Store" }); - - // Act - var pathData = route.GetVirtualPath(context); - - // Assert - Assert.Equal(new PathString("/Home/Store"), pathData.VirtualPath); - Assert.Same(route, pathData.Router); - Assert.Empty(pathData.DataTokens); - - Assert.Equal(expectedValues, childContext.ProvidedValues); - } - - [Fact] - public void GetVirtualPath_Sends_ProvidedValues_IncludingDefaults() - { - // Arrange - VirtualPathContext childContext = null; - var target = new Mock(MockBehavior.Strict); - target - .Setup(r => r.GetVirtualPath(It.IsAny())) - .Callback(c => { childContext = c; c.IsBound = true; }) - .Returns(null); - - var route = CreateRoute(target.Object, "Admin/{controller}/{action}", new { area = "Admin" }); - var context = CreateVirtualPathContext( - new { action = "Store" }, new { Controller = "Home", action = "Blog" }); - - var expectedValues = new RouteValueDictionary( - new { controller = "Home", action = "Store", area = "Admin" }); - - // Act - var pathData = route.GetVirtualPath(context); - - // Assert - Assert.Equal(new PathString("/Admin/Home/Store"), pathData.VirtualPath); - Assert.Same(route, pathData.Router); - Assert.Empty(pathData.DataTokens); - - Assert.Equal(expectedValues, childContext.ProvidedValues); - } - - [Fact] - public void GetVirtualPath_Sends_ProvidedValues_ButNotQueryStringValues() - { - // Arrange - VirtualPathContext childContext = null; - var target = new Mock(MockBehavior.Strict); - target - .Setup(r => r.GetVirtualPath(It.IsAny())) - .Callback(c => { childContext = c; c.IsBound = true; }) - .Returns(null); - - var route = CreateRoute(target.Object, "{controller}/{action}"); - var context = CreateVirtualPathContext( - new { action = "Store", id = 5 }, new { Controller = "Home", action = "Blog" }); - - var expectedValues = new RouteValueDictionary(new { controller = "Home", action = "Store" }); - - // Act - var pathData = route.GetVirtualPath(context); - - // Assert - Assert.Equal(new PathString("/Home/Store?id=5"), pathData.VirtualPath); - Assert.Same(route, pathData.Router); - Assert.Empty(pathData.DataTokens); - - Assert.Equal(expectedValues, childContext.ProvidedValues); - } - // Any ambient values from the current request should be visible to constraint, even // if they have nothing to do with the route generating a link [Fact] @@ -1930,7 +1833,6 @@ namespace Microsoft.AspNet.Routing.Template var target = new Mock(MockBehavior.Strict); target .Setup(e => e.GetVirtualPath(It.IsAny())) - .Callback(c => c.IsBound = handleRequest) .Returns(rc => null); target diff --git a/test/Microsoft.AspNet.Routing.Tests/Tree/TreeRouterTest.cs b/test/Microsoft.AspNet.Routing.Tests/Tree/TreeRouterTest.cs index 341b015602..768f59cd55 100644 --- a/test/Microsoft.AspNet.Routing.Tests/Tree/TreeRouterTest.cs +++ b/test/Microsoft.AspNet.Routing.Tests/Tree/TreeRouterTest.cs @@ -376,22 +376,13 @@ namespace Microsoft.AspNet.Routing.Tree public void TreeRouter_GenerateLink(string firstTemplate, string secondTemplate, string expectedPath) { // Arrange - var expectedGroup = CreateRouteGroup(0, firstTemplate); - - string selectedGroup = null; - Action callback = ctx => - { - selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey]; - ctx.IsBound = true; - }; - var values = new Dictionary { {"url", "dingo" }, {"id", 5 } }; - var route = CreateAttributeRoute(callback, firstTemplate, secondTemplate); + var route = CreateAttributeRoute(firstTemplate, secondTemplate); var context = CreateVirtualPathContext( values: values, ambientValues: null); @@ -404,7 +395,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString(expectedPath), result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - Assert.Equal(expectedGroup, selectedGroup); } [Fact] @@ -414,16 +404,7 @@ namespace Microsoft.AspNet.Routing.Tree var firstTemplate = "template"; var secondTemplate = "template/{parameter:int=1003}"; - var expectedGroup = CreateRouteGroup(0, secondTemplate); - - string selectedGroup = null; - Action callback = ctx => - { - selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey]; - ctx.IsBound = true; - }; - - var route = CreateAttributeRoute(callback, firstTemplate, secondTemplate); + var route = CreateAttributeRoute(firstTemplate, secondTemplate); var context = CreateVirtualPathContext( values: null, ambientValues: null); @@ -437,10 +418,8 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString($"/template"), result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - // Even though the path was /template, the group generated from was /template/{paramter:int=1003} - Assert.Equal(expectedGroup, selectedGroup); } - + [Theory] [InlineData("template/{parameter:int=5}", "template", "/template/5")] [InlineData("template/{parameter}", "template", "/template/5")] @@ -450,16 +429,8 @@ namespace Microsoft.AspNet.Routing.Tree string secondTemplate, string expectedPath) { - var expectedGroup = CreateRouteGroup(0, firstTemplate); - - string selectedGroup = null; - Action callback = ctx => - { - selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey]; - ctx.IsBound = true; - }; - - var route = CreateAttributeRoute(callback, firstTemplate, secondTemplate); + // Arrange + var route = CreateAttributeRoute(firstTemplate, secondTemplate); var parameter = 5; var id = 1234; var values = new Dictionary @@ -479,7 +450,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString(expectedPath), result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - Assert.Equal(expectedGroup, selectedGroup); } [Theory] @@ -492,16 +462,7 @@ namespace Microsoft.AspNet.Routing.Tree string expectedPath) { // Arrange - var expectedGroup = CreateRouteGroup(0, secondTemplate); - - string selectedGroup = null; - Action callback = ctx => - { - selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey]; - ctx.IsBound = true; - }; - - var route = CreateAttributeRoute(callback, firstTemplate, secondTemplate); + var route = CreateAttributeRoute(firstTemplate, secondTemplate); var parameter = 5; var id = 1234; var values = new Dictionary @@ -510,7 +471,7 @@ namespace Microsoft.AspNet.Routing.Tree { nameof(id), id } }; var context = CreateVirtualPathContext( - values: null, + values: null, ambientValues: values); // Act @@ -521,7 +482,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString(expectedPath), result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - Assert.Equal(expectedGroup, selectedGroup); } [Theory] @@ -538,18 +498,6 @@ namespace Microsoft.AspNet.Routing.Tree public void TreeRouter_GenerateLink_RespectsPrecedence(string firstTemplate, string secondTemplate) { // Arrange - var expectedGroup = CreateRouteGroup(0, firstTemplate); - - string selectedGroup = null; - - var next = new Mock(); - next.Setup(n => n.GetVirtualPath(It.IsAny())).Callback(ctx => - { - selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey]; - ctx.IsBound = true; - }) - .Returns((VirtualPathData)null); - var matchingRoutes = Enumerable.Empty(); var firstEntry = CreateGenerationEntry(firstTemplate, requiredValues: null); @@ -559,7 +507,7 @@ namespace Microsoft.AspNet.Routing.Tree // try to generate a link, the route with a higher precedence gets tried first. var linkGenerationEntries = new[] { secondEntry, firstEntry }; - var route = CreateAttributeRoute(next.Object, matchingRoutes, linkGenerationEntries); + var route = CreateAttributeRoute(matchingRoutes, linkGenerationEntries); var context = CreateVirtualPathContext(values: null, ambientValues: new { parameter = 5 }); @@ -571,8 +519,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString("/template/5"), result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - - Assert.Equal(expectedGroup, selectedGroup); } [Theory] @@ -591,25 +537,13 @@ namespace Microsoft.AspNet.Routing.Tree object parameter) { // Arrange - var expectedGroup = CreateRouteGroup(0, template); - - string selectedGroup = null; - - var next = new Mock(); - next.Setup(n => n.GetVirtualPath(It.IsAny())).Callback(ctx => - { - selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey]; - ctx.IsBound = true; - }) - .Returns((VirtualPathData)null); - var matchingRoutes = Enumerable.Empty(); var entry = CreateGenerationEntry(template, requiredValues: null); var linkGenerationEntries = new[] { entry }; - var route = CreateAttributeRoute(next.Object, matchingRoutes, linkGenerationEntries); + var route = CreateAttributeRoute(matchingRoutes, linkGenerationEntries); VirtualPathContext context; if (parameter != null) @@ -652,17 +586,6 @@ namespace Microsoft.AspNet.Routing.Tree public void TreeRouter_GenerateLink_RespectsOrderOverPrecedence(string firstTemplate, string secondTemplate) { // Arrange - var selectedGroup = CreateRouteGroup(0, secondTemplate); - - string firstRouteGroupSelected = null; - var next = new Mock(); - next.Setup(n => n.GetVirtualPath(It.IsAny())).Callback(ctx => - { - firstRouteGroupSelected = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey]; - ctx.IsBound = true; - }) - .Returns((VirtualPathData)null); - var matchingRoutes = Enumerable.Empty(); var firstRoute = CreateGenerationEntry(firstTemplate, requiredValues: null, order: 1); @@ -673,7 +596,7 @@ namespace Microsoft.AspNet.Routing.Tree // relative order gets tried first. var linkGenerationEntries = new[] { firstRoute, secondRoute }; - var route = CreateAttributeRoute(next.Object, matchingRoutes, linkGenerationEntries); + var route = CreateAttributeRoute(matchingRoutes, linkGenerationEntries); var context = CreateVirtualPathContext(null, ambientValues: new { parameter = 5 }); @@ -685,8 +608,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString("/template/5"), result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - - Assert.Equal(selectedGroup, firstRouteGroupSelected); } [Theory] @@ -698,17 +619,6 @@ namespace Microsoft.AspNet.Routing.Tree public void TreeRouter_GenerateLink_RespectsOrder(string firstTemplate, string secondTemplate) { // Arrange - var expectedGroup = CreateRouteGroup(0, secondTemplate); - - var next = new Mock(); - string selectedGroup = null; - next.Setup(n => n.GetVirtualPath(It.IsAny())).Callback(ctx => - { - selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey]; - ctx.IsBound = true; - }) - .Returns((VirtualPathData)null); - var matchingRoutes = Enumerable.Empty(); var firstRoute = CreateGenerationEntry(firstTemplate, requiredValues: null, order: 1); @@ -718,7 +628,7 @@ namespace Microsoft.AspNet.Routing.Tree // we try to generate a link, the route with the higher relative order gets tried first. var linkGenerationEntries = new[] { firstRoute, secondRoute }; - var route = CreateAttributeRoute(next.Object, matchingRoutes, linkGenerationEntries); + var route = CreateAttributeRoute(matchingRoutes, linkGenerationEntries); var context = CreateVirtualPathContext(values: null, ambientValues: new { first = 5, second = 5 }); @@ -730,8 +640,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString("/template/5"), result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - - Assert.Equal(expectedGroup, selectedGroup); } [Theory] @@ -743,17 +651,6 @@ namespace Microsoft.AspNet.Routing.Tree public void TreeRouter_GenerateLink_EnsuresStableOrder(string firstTemplate, string secondTemplate) { // Arrange - var expectedGroup = CreateRouteGroup(0, firstTemplate); - - var next = new Mock(); - string selectedGroup = null; - next.Setup(n => n.GetVirtualPath(It.IsAny())).Callback(ctx => - { - selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey]; - ctx.IsBound = true; - }) - .Returns((VirtualPathData)null); - var matchingRoutes = Enumerable.Empty(); var firstRoute = CreateGenerationEntry(firstTemplate, requiredValues: null, order: 0); @@ -763,7 +660,7 @@ namespace Microsoft.AspNet.Routing.Tree // we try to generate a link, the route with the higher template order gets tried first. var linkGenerationEntries = new[] { secondRoute, firstRoute }; - var route = CreateAttributeRoute(next.Object, matchingRoutes, linkGenerationEntries); + var route = CreateAttributeRoute(matchingRoutes, linkGenerationEntries); var context = CreateVirtualPathContext(values: null, ambientValues: new { first = 5, second = 5 }); @@ -775,8 +672,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString("/first/5"), result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - - Assert.Equal(expectedGroup, selectedGroup); } public static IEnumerable NamedEntriesWithDifferentTemplates @@ -880,20 +775,9 @@ namespace Microsoft.AspNet.Routing.Tree var expectedLink = new PathString( namedEntries.First().Template.Parameters.Any() ? "/template/5" : "/template"); - var expectedGroup = "0&" + namedEntries.First().Template.TemplateText; - string selectedGroup = null; - var next = new Mock(); - next.Setup(s => s.GetVirtualPath(It.IsAny())) - .Callback(vpc => - { - vpc.IsBound = true; - selectedGroup = (string)vpc.ProvidedValues[TreeRouter.RouteGroupKey]; - }) - .Returns((VirtualPathData)null); - var matchingEntries = Enumerable.Empty(); - var route = CreateAttributeRoute(next.Object, matchingEntries, namedEntries); + var route = CreateAttributeRoute(matchingEntries, namedEntries); var ambientValues = namedEntries.First().Template.Parameters.Any() ? new { parameter = 5 } : null; @@ -907,24 +791,12 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(expectedLink, result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - - Assert.Equal(expectedGroup, selectedGroup); } [Fact] public void TreeRouter_GenerateLink_WithName() { // Arrange - string selectedGroup = null; - var next = new Mock(); - next.Setup(s => s.GetVirtualPath(It.IsAny())) - .Callback(vpc => - { - vpc.IsBound = true; - selectedGroup = (string)vpc.ProvidedValues[TreeRouter.RouteGroupKey]; - }) - .Returns((VirtualPathData)null); - var namedEntry = CreateGenerationEntry("named", requiredValues: null, order: 1, name: "NamedRoute"); var unnamedEntry = CreateGenerationEntry("unnamed", requiredValues: null, order: 0); @@ -934,7 +806,7 @@ namespace Microsoft.AspNet.Routing.Tree var matchingEntries = Enumerable.Empty(); - var route = CreateAttributeRoute(next.Object, matchingEntries, linkGenerationEntries); + var route = CreateAttributeRoute(matchingEntries, linkGenerationEntries); var context = CreateVirtualPathContext(values: null, ambientValues: null, name: "NamedRoute"); @@ -946,23 +818,12 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString("/named"), result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - - Assert.Equal("1&named", selectedGroup); } [Fact] public void TreeRouter_DoesNotGenerateLink_IfThereIsNoRouteForAGivenName() { // Arrange - string selectedGroup = null; - var next = new Mock(); - next.Setup(s => s.GetVirtualPath(It.IsAny())) - .Callback(vpc => - { - vpc.IsBound = true; - selectedGroup = (string)vpc.ProvidedValues[TreeRouter.RouteGroupKey]; - }); - var namedEntry = CreateGenerationEntry("named", requiredValues: null, order: 1, name: "NamedRoute"); // Add an unnamed entry to ensure we don't fall back to generating a link for an unnamed route. @@ -974,7 +835,7 @@ namespace Microsoft.AspNet.Routing.Tree var matchingEntries = Enumerable.Empty(); - var route = CreateAttributeRoute(next.Object, matchingEntries, linkGenerationEntries); + var route = CreateAttributeRoute(matchingEntries, linkGenerationEntries); var context = CreateVirtualPathContext(values: null, ambientValues: null, name: "NonExistingNamedRoute"); @@ -994,15 +855,6 @@ namespace Microsoft.AspNet.Routing.Tree public void TreeRouter_DoesNotGenerateLink_IfValuesDoNotMatchNamedEntry(string template, string value) { // Arrange - string selectedGroup = null; - var next = new Mock(); - next.Setup(s => s.GetVirtualPath(It.IsAny())) - .Callback(vpc => - { - vpc.IsBound = true; - selectedGroup = (string)vpc.ProvidedValues[TreeRouter.RouteGroupKey]; - }); - var namedEntry = CreateGenerationEntry(template, requiredValues: null, order: 1, name: "NamedRoute"); // Add an unnamed entry to ensure we don't fall back to generating a link for an unnamed route. @@ -1014,7 +866,7 @@ namespace Microsoft.AspNet.Routing.Tree var matchingEntries = Enumerable.Empty(); - var route = CreateAttributeRoute(next.Object, matchingEntries, linkGenerationEntries); + var route = CreateAttributeRoute(matchingEntries, linkGenerationEntries); var ambientValues = value == null ? null : new { parameter = value }; @@ -1035,14 +887,9 @@ namespace Microsoft.AspNet.Routing.Tree public void TreeRouter_GeneratesLink_IfValuesMatchNamedEntry(string template, string value) { // Arrange - string selectedGroup = null; var next = new Mock(); - next.Setup(s => s.GetVirtualPath(It.IsAny())) - .Callback(vpc => - { - vpc.IsBound = true; - selectedGroup = (string)vpc.ProvidedValues[TreeRouter.RouteGroupKey]; - }) + next + .Setup(s => s.GetVirtualPath(It.IsAny())) .Returns((VirtualPathData)null); var namedEntry = CreateGenerationEntry(template, requiredValues: null, order: 1, name: "NamedRoute"); @@ -1070,11 +917,9 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString("/template/5"), result.VirtualPath); Assert.Same(route, result.Router); Assert.Empty(result.DataTokens); - - Assert.Equal(string.Format("1&{0}", template), selectedGroup); } - [Fact] + [Fact] public void TreeRouter_GenerateLink_NoRequiredValues() { // Arrange @@ -1174,14 +1019,6 @@ namespace Microsoft.AspNet.Routing.Tree "api/{area}/dosomething/{controller}/{action}", new { action = "Index", controller = "Store", area = "AwesomeCo" }); - var expectedValues = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "area", "AwesomeCo" }, - { "controller", "Store" }, - { "action", "Index" }, - { TreeRouter.RouteGroupKey, entry.RouteGroup }, - }; - var next = new StubRouter(); var route = CreateAttributeRoute(next, entry); @@ -1197,8 +1034,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString("/api/AwesomeCo/dosomething/Store/Index"), pathData.VirtualPath); Assert.Same(route, pathData.Router); Assert.Empty(pathData.DataTokens); - - Assert.Equal(expectedValues, next.GenerationContext.ProvidedValues); } [Fact] @@ -1226,13 +1061,6 @@ namespace Microsoft.AspNet.Routing.Tree // Arrange var entry = CreateGenerationEntry("api/Store/{action}/{id:int}", new { action = "Index", controller = "Store" }); - var expectedValues = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "action", "Index" }, - { "id", 5 }, - { TreeRouter.RouteGroupKey, entry.RouteGroup }, - }; - var next = new StubRouter(); var route = CreateAttributeRoute(next, entry); @@ -1246,8 +1074,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Equal(new PathString("/api/Store/Index/5"), pathData.VirtualPath); Assert.Same(route, pathData.Router); Assert.Empty(pathData.DataTokens); - - Assert.Equal(expectedValues, next.GenerationContext.ProvidedValues); } [Fact] @@ -1257,12 +1083,6 @@ namespace Microsoft.AspNet.Routing.Tree var entry = CreateGenerationEntry("api/Store/{action}/{id:int}", new { action = "Index", controller = "Store" }); var route = CreateAttributeRoute(entry); - var expectedValues = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "id", "5" }, - { TreeRouter.RouteGroupKey, entry.RouteGroup }, - }; - var next = new StubRouter(); var context = CreateVirtualPathContext(new { action = "Index", controller = "Store", id = "heyyyy" }); @@ -1311,29 +1131,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Empty(pathData.DataTokens); } - [Fact] - public void TreeRouter_GenerateLink_ForwardsRouteGroup() - { - // Arrange - var entry = CreateGenerationEntry("api/Store", new { action = "Index", controller = "Store" }); - - var expectedValues = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { TreeRouter.RouteGroupKey, entry.RouteGroup }, - }; - - var next = new StubRouter(); - var route = CreateAttributeRoute(next, entry); - - var context = CreateVirtualPathContext(new { action = "Index", controller = "Store" }); - - // Act - var path = route.GetVirtualPath(context); - - // Assert - Assert.Equal(expectedValues, next.GenerationContext.ProvidedValues); - } - [Fact] public void TreeRouter_GenerateLink_RejectedByFirstRoute() { @@ -1355,41 +1152,6 @@ namespace Microsoft.AspNet.Routing.Tree Assert.Empty(pathData.DataTokens); } - [Fact] - public void TreeRouter_GenerateLink_RejectedByHandler() - { - // Arrange - var entry1 = CreateGenerationEntry("api/Store", new { action = "Edit", controller = "Store" }); - var entry2 = CreateGenerationEntry("api2/{controller}", new { action = "Edit", controller = "Store" }); - - var next = new StubRouter(); - - var callCount = 0; - next.GenerationDelegate = (VirtualPathContext c) => - { - // Reject entry 1. - callCount++; - return !c.ProvidedValues.Contains(new KeyValuePair( - TreeRouter.RouteGroupKey, - entry1.RouteGroup)); - }; - - var route = CreateAttributeRoute(next, entry1, entry2); - - var context = CreateVirtualPathContext(new { action = "Edit", controller = "Store" }); - - // Act - var pathData = route.GetVirtualPath(context); - - // Assert - Assert.NotNull(pathData); - Assert.Equal(new PathString("/api2/Store"), pathData.VirtualPath); - Assert.Same(route, pathData.Router); - Assert.Empty(pathData.DataTokens); - - Assert.Equal(2, callCount); - } - [Fact] public void TreeRouter_GenerateLink_ToArea() { @@ -1509,7 +1271,7 @@ namespace Microsoft.AspNet.Routing.Tree // values new object[] { - "Test/{val1}/{val2}.{val3?}", + "Test/{val1}/{val2}.{val3?}", new {val1 = "someval1", val2 = "someval2", val3 = "someval3a"}, new {val3 = "someval3v"}, "/Test/someval1/someval2.someval3v", @@ -1527,18 +1289,18 @@ namespace Microsoft.AspNet.Routing.Tree null, new {val1 = "someval1", val2 = "someval2" }, "/Test/someval1/someval2", - }, + }, new object[] { "Test/{val1}.{val2}.{val3}.{val4?}", - new {val1 = "someval1", val2 = "someval2" }, + new {val1 = "someval1", val2 = "someval2" }, new {val4 = "someval4", val3 = "someval3" }, "/Test/someval1.someval2.someval3.someval4", }, new object[] { "Test/{val1}.{val2}.{val3}.{val4?}", - new {val1 = "someval1", val2 = "someval2" }, + new {val1 = "someval1", val2 = "someval2" }, new {val3 = "someval3" }, "/Test/someval1.someval2.someval3", }, @@ -1931,6 +1693,13 @@ namespace Microsoft.AspNet.Routing.Tree Enumerable.Empty()); } + private static TreeRouter CreateAttributeRoute( + IEnumerable matchingEntries, + IEnumerable generationEntries) + { + return CreateAttributeRoute(new StubRouter(), matchingEntries, generationEntries); + } + private static TreeRouter CreateAttributeRoute( IRouter next, IEnumerable matchingEntries, @@ -1952,13 +1721,13 @@ namespace Microsoft.AspNet.Routing.Tree } private static TreeRouter CreateAttributeRoute( - Action virtualPathCallback, string firstTemplate, string secondTemplate) { var next = new Mock(); - next.Setup(n => n.GetVirtualPath(It.IsAny())).Callback(virtualPathCallback) - .Returns((VirtualPathData)null); + next + .Setup(n => n.GetVirtualPath(It.IsAny())) + .Returns((VirtualPathData)null); var matchingRoutes = Enumerable.Empty(); var firstEntry = CreateGenerationEntry(firstTemplate, requiredValues: null); @@ -2014,8 +1783,6 @@ namespace Microsoft.AspNet.Routing.Tree { public VirtualPathContext GenerationContext { get; set; } - public Func GenerationDelegate { get; set; } - public RouteContext MatchingContext { get; set; } public Func MatchingDelegate { get; set; } @@ -2023,16 +1790,6 @@ namespace Microsoft.AspNet.Routing.Tree public VirtualPathData GetVirtualPath(VirtualPathContext context) { GenerationContext = context; - - if (GenerationDelegate == null) - { - context.IsBound = true; - } - else - { - context.IsBound = GenerationDelegate(context); - } - return null; }