Simplify link generation code

- Removes ProvidedValues and IsBound

- Removes best-effort link generation

- simplify code where possible

- lots of test simplification
This commit is contained in:
Ryan Nowak 2015-12-03 18:29:18 -08:00
parent c911a10692
commit b01183f023
9 changed files with 141 additions and 863 deletions

View File

@ -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;
}
}

View File

@ -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
{
/// <summary>
/// A context for virtual path generation operations.
/// </summary>
public class VirtualPathContext
{
/// <summary>
/// Creates a new <see cref="VirtualPathContext"/>.
/// </summary>
/// <param name="httpContext">The <see cref="Http.HttpContext"/> associated with the current request.</param>
/// <param name="ambientValues">The set of route values associated with the current request.</param>
/// <param name="values">The set of new values provided for virtual path generation.</param>
public VirtualPathContext(
HttpContext httpContext,
RouteValueDictionary ambientValues,
@ -16,28 +24,43 @@ namespace Microsoft.AspNet.Routing
{
}
/// <summary>
/// Creates a new <see cref="VirtualPathContext"/>.
/// </summary>
/// <param name="httpContext">The <see cref="Http.HttpContext"/> associated with the current request.</param>
/// <param name="ambientValues">The set of route values associated with the current request.</param>
/// <param name="values">The set of new values provided for virtual path generation.</param>
/// <param name="routeName">The name of the route to use for virtual path generation.</param>
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<string, object> ProvidedValues { get; set; }
/// <summary>
/// Gets the set of route values associated with the current request.
/// </summary>
public RouteValueDictionary AmbientValues { get; }
public HttpContext Context { get; }
/// <summary>
/// Gets the <see cref="Http.HttpContext"/> associated with the current request.
/// </summary>
public HttpContext HttpContext { get; }
public bool IsBound { get; set; }
/// <summary>
/// Gets the name of the route to use for virtual path generation.
/// </summary>
public string RouteName { get; }
/// <summary>
/// Gets the set of new values provided for virtual path generation.
/// </summary>
public RouteValueDictionary Values { get; }
}
}

View File

@ -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<IRouter> 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)

View File

@ -69,13 +69,5 @@ namespace Microsoft.AspNet.Routing
{"required", typeof(RequiredRouteConstraint) },
};
}
/// <summary>
/// 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.
/// </summary>
public bool UseBestEffortLinkGeneration { get; set; }
}
}

View File

@ -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<string, object> 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<string, IRouteConstraint> GetConstraints(
IInlineConstraintResolver inlineConstraintResolver,
string template,

View File

@ -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<string, object>(
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)

View File

@ -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<InvalidOperationException>(() => 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<INamedRouter>(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<INamedRouter>(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<INamedRouter>(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<VirtualPathContext>()), Times.Once());
route2.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
route3.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), 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<VirtualPathContext>()), Times.Once());
route2.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
route3.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), 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<VirtualPathContext>()), Times.Once());
route2.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
route3.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), 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<VirtualPathContext>()), Times.Once());
route2.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
route3.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), 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<VirtualPathContext>()), Times.Once());
route2.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
route3.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), 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<VirtualPathContext>()), Times.Once());
route2.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
route3.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
}
// "Integration" tests for RouteCollection
public static IEnumerable<object[]> 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<VirtualPathContext>()), Times.Once());
route2.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
route3.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
}
// DataTokens test data for RouterCollection.GetVirtualPath
public static IEnumerable<object[]> DataTokensTestData
{
@ -744,7 +475,6 @@ namespace Microsoft.AspNet.Routing
var target = new Mock<INamedRouter>(MockBehavior.Strict);
target
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(c => c.IsBound = accept && c.RouteName == name)
.Returns<VirtualPathContext>(c =>
c.RouteName == name ? new VirtualPathData(target.Object, matchValue) : null)
.Verifiable();
@ -770,7 +500,6 @@ namespace Microsoft.AspNet.Routing
var target = new Mock<IRouter>(MockBehavior.Strict);
target
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(c => c.IsBound = true)
.Returns<VirtualPathContext>(rc => null);
var resolverMock = new Mock<IInlineConstraintResolver>();
@ -876,7 +605,6 @@ namespace Microsoft.AspNet.Routing
var target = new Mock<IRouter>(MockBehavior.Strict);
target
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(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;

View File

@ -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<IRouter>(MockBehavior.Strict);
target
.Setup(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(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<IRouter>(MockBehavior.Strict);
target
.Setup(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(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<IRouter>(MockBehavior.Strict);
target
.Setup(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(c => { childContext = c; c.IsBound = true; })
.Returns<string>(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<IRouter>(MockBehavior.Strict);
target
.Setup(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(c => { childContext = c; c.IsBound = true; })
.Returns<string>(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<IRouter>(MockBehavior.Strict);
target
.Setup(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(c => { childContext = c; c.IsBound = true; })
.Returns<string>(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<IRouter>(MockBehavior.Strict);
target
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(c => c.IsBound = handleRequest)
.Returns<VirtualPathContext>(rc => null);
target

View File

@ -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<VirtualPathContext> callback = ctx =>
{
selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey];
ctx.IsBound = true;
};
var values = new Dictionary<string, object>
{
{"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<VirtualPathContext> 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<VirtualPathContext> 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<string, object>
@ -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<VirtualPathContext> 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<string, object>
@ -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<IRouter>();
next.Setup(n => n.GetVirtualPath(It.IsAny<VirtualPathContext>())).Callback<VirtualPathContext>(ctx =>
{
selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey];
ctx.IsBound = true;
})
.Returns((VirtualPathData)null);
var matchingRoutes = Enumerable.Empty<TreeRouteMatchingEntry>();
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<IRouter>();
next.Setup(n => n.GetVirtualPath(It.IsAny<VirtualPathContext>())).Callback<VirtualPathContext>(ctx =>
{
selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey];
ctx.IsBound = true;
})
.Returns((VirtualPathData)null);
var matchingRoutes = Enumerable.Empty<TreeRouteMatchingEntry>();
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<IRouter>();
next.Setup(n => n.GetVirtualPath(It.IsAny<VirtualPathContext>())).Callback<VirtualPathContext>(ctx =>
{
firstRouteGroupSelected = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey];
ctx.IsBound = true;
})
.Returns((VirtualPathData)null);
var matchingRoutes = Enumerable.Empty<TreeRouteMatchingEntry>();
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<IRouter>();
string selectedGroup = null;
next.Setup(n => n.GetVirtualPath(It.IsAny<VirtualPathContext>())).Callback<VirtualPathContext>(ctx =>
{
selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey];
ctx.IsBound = true;
})
.Returns((VirtualPathData)null);
var matchingRoutes = Enumerable.Empty<TreeRouteMatchingEntry>();
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<IRouter>();
string selectedGroup = null;
next.Setup(n => n.GetVirtualPath(It.IsAny<VirtualPathContext>())).Callback<VirtualPathContext>(ctx =>
{
selectedGroup = (string)ctx.ProvidedValues[TreeRouter.RouteGroupKey];
ctx.IsBound = true;
})
.Returns((VirtualPathData)null);
var matchingRoutes = Enumerable.Empty<TreeRouteMatchingEntry>();
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<object[]> 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<IRouter>();
next.Setup(s => s.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(vpc =>
{
vpc.IsBound = true;
selectedGroup = (string)vpc.ProvidedValues[TreeRouter.RouteGroupKey];
})
.Returns((VirtualPathData)null);
var matchingEntries = Enumerable.Empty<TreeRouteMatchingEntry>();
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<IRouter>();
next.Setup(s => s.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(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<TreeRouteMatchingEntry>();
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<IRouter>();
next.Setup(s => s.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(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<TreeRouteMatchingEntry>();
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<IRouter>();
next.Setup(s => s.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(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<TreeRouteMatchingEntry>();
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<IRouter>();
next.Setup(s => s.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(vpc =>
{
vpc.IsBound = true;
selectedGroup = (string)vpc.ProvidedValues[TreeRouter.RouteGroupKey];
})
next
.Setup(s => s.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.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<string, object>(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<string, object>(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<string, object>(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<string, object>(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<string, object>(
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<TreeRouteLinkGenerationEntry>());
}
private static TreeRouter CreateAttributeRoute(
IEnumerable<TreeRouteMatchingEntry> matchingEntries,
IEnumerable<TreeRouteLinkGenerationEntry> generationEntries)
{
return CreateAttributeRoute(new StubRouter(), matchingEntries, generationEntries);
}
private static TreeRouter CreateAttributeRoute(
IRouter next,
IEnumerable<TreeRouteMatchingEntry> matchingEntries,
@ -1952,13 +1721,13 @@ namespace Microsoft.AspNet.Routing.Tree
}
private static TreeRouter CreateAttributeRoute(
Action<VirtualPathContext> virtualPathCallback,
string firstTemplate,
string secondTemplate)
{
var next = new Mock<IRouter>();
next.Setup(n => n.GetVirtualPath(It.IsAny<VirtualPathContext>())).Callback<VirtualPathContext>(virtualPathCallback)
.Returns((VirtualPathData)null);
next
.Setup(n => n.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Returns((VirtualPathData)null);
var matchingRoutes = Enumerable.Empty<TreeRouteMatchingEntry>();
var firstEntry = CreateGenerationEntry(firstTemplate, requiredValues: null);
@ -2014,8 +1783,6 @@ namespace Microsoft.AspNet.Routing.Tree
{
public VirtualPathContext GenerationContext { get; set; }
public Func<VirtualPathContext, bool> GenerationDelegate { get; set; }
public RouteContext MatchingContext { get; set; }
public Func<RouteContext, bool> 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;
}