Introducing VirtualPathData for IRouter.GetVirtualPath

This commit is contained in:
ianhong 2015-03-10 11:23:50 -07:00
parent 0db1f1a42f
commit 3693002b02
10 changed files with 578 additions and 143 deletions

View File

@ -23,7 +23,7 @@ namespace RoutingSample.Web
context.IsHandled = true;
}
public string GetVirtualPath(VirtualPathContext context)
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
// We don't really care what the values look like.
context.IsBound = true;

View File

@ -52,9 +52,9 @@ namespace RoutingSample.Web
}
}
public string GetVirtualPath(VirtualPathContext context)
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
return null;
}
}
}
}

View File

@ -9,6 +9,6 @@ namespace Microsoft.AspNet.Routing
{
Task RouteAsync(RouteContext context);
string GetVirtualPath(VirtualPathContext context);
VirtualPathData GetVirtualPath(VirtualPathContext context);
}
}

View File

@ -98,11 +98,11 @@ namespace Microsoft.AspNet.Routing
}
}
public virtual string GetVirtualPath(VirtualPathContext context)
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
// 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;
@ -110,11 +110,11 @@ namespace Microsoft.AspNet.Routing
if (!string.IsNullOrEmpty(context.RouteName))
{
var isValidated = false;
string bestPath = null;
VirtualPathData bestPathData = null;
INamedRouter matchedNamedRoute;
if (_namedRoutes.TryGetValue(context.RouteName, out matchedNamedRoute))
{
bestPath = matchedNamedRoute.GetVirtualPath(context);
bestPathData = matchedNamedRoute.GetVirtualPath(context);
isValidated = context.IsBound;
}
@ -125,13 +125,13 @@ namespace Microsoft.AspNet.Routing
// reset because we're sharing the context
context.IsBound = false;
var path = unnamedRoute.GetVirtualPath(context);
if (path == null)
var pathData = unnamedRoute.GetVirtualPath(context);
if (pathData == null)
{
continue;
}
if (bestPath != null)
if (bestPathData != null)
{
// There was already a previous route which matched the name.
throw new InvalidOperationException(
@ -140,15 +140,15 @@ namespace Microsoft.AspNet.Routing
else if (context.IsBound)
{
// This is the first 'validated' match that we've found.
bestPath = path;
bestPathData = pathData;
isValidated = true;
}
else
{
Debug.Assert(bestPath == null);
Debug.Assert(bestPathData == null);
// This is the first 'unvalidated' match that we've found.
bestPath = path;
bestPathData = pathData;
isValidated = false;
}
}
@ -156,7 +156,16 @@ namespace Microsoft.AspNet.Routing
if (isValidated || useBestEffort)
{
context.IsBound = isValidated;
return NormalizeVirtualPath(bestPath);
if (bestPathData != null)
{
bestPathData = new VirtualPathData(
bestPathData.Router,
NormalizeVirtualPath(bestPathData.VirtualPath),
bestPathData.DataTokens);
}
return bestPathData;
}
else
{
@ -165,13 +174,13 @@ namespace Microsoft.AspNet.Routing
}
else
{
string bestPath = null;
VirtualPathData bestPathData = null;
for (var i = 0; i < Count; i++)
{
var route = this[i];
var path = route.GetVirtualPath(context);
if (path == null)
var pathData = route.GetVirtualPath(context);
if (pathData == null)
{
continue;
}
@ -179,18 +188,24 @@ namespace Microsoft.AspNet.Routing
if (context.IsBound)
{
// This route has validated route values, short circuit.
return NormalizeVirtualPath(path);
return new VirtualPathData(
pathData.Router,
NormalizeVirtualPath(pathData.VirtualPath),
pathData.DataTokens);
}
else if (bestPath == null)
else if (bestPathData == null)
{
// The values aren't validated, but this is the best we've seen so far
bestPath = path;
bestPathData = pathData;
}
}
if (useBestEffort)
{
return NormalizeVirtualPath(bestPath);
return new VirtualPathData(
bestPathData.Router,
NormalizeVirtualPath(bestPathData.VirtualPath),
bestPathData.DataTokens);
}
else
{
@ -223,9 +238,9 @@ namespace Microsoft.AspNet.Routing
}
return url;
}
}
private void EnsureLogger(HttpContext context)
private void EnsureLogger(HttpContext context)
{
if (_logger == null)
{

View File

@ -181,7 +181,7 @@ namespace Microsoft.AspNet.Routing.Template
}
}
public virtual string GetVirtualPath(VirtualPathContext context)
public virtual VirtualPathData GetVirtualPath(VirtualPathContext context)
{
var values = _binder.GetValues(context.AmbientValues, context.Values);
if (values == null)
@ -204,19 +204,35 @@ namespace Microsoft.AspNet.Routing.Template
// Validate that the target can accept these values.
var childContext = CreateChildVirtualPathContext(context, values.AcceptedValues);
var path = _target.GetVirtualPath(childContext);
if (path != null)
var pathData = _target.GetVirtualPath(childContext);
if (pathData != null)
{
// If the target generates a value then that can short circuit.
return path;
return pathData;
}
// If we can produce a value go ahead and do it, the caller can check context.IsBound
// to see if the values were validated.
path = _binder.BindValues(values.AcceptedValues);
// When we still cannot produce a value, this should return null.
var tempPath = _binder.BindValues(values.AcceptedValues);
if (tempPath == null)
{
return null;
}
pathData = new VirtualPathData(this, tempPath);
if (DataTokens != null)
{
foreach (var dataToken in DataTokens)
{
pathData.DataTokens.Add(dataToken.Key, dataToken.Value);
}
}
context.IsBound = childContext.IsBound;
return path;
return pathData;
}
private VirtualPathContext CreateChildVirtualPathContext(

View File

@ -0,0 +1,80 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Internal;
namespace Microsoft.AspNet.Routing
{
/// <summary>
/// Represents information about the route and virtual path that are the result of
/// generating a URL with the ASP.NET routing middleware.
/// </summary>
public class VirtualPathData
{
private string _virtualPath;
private readonly IDictionary<string, object> _dataToken;
/// <summary>
/// Initializes a new instance of the <see cref="VirtualPathData"/> class.
/// </summary>
/// <param name="router">The object that is used to generate the URL.</param>
/// <param name="virtualPath">The generated URL.</param>
public VirtualPathData([NotNull] IRouter router, string virtualPath)
: this(router, virtualPath, dataTokens: new RouteValueDictionary())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="VirtualPathData"/> class.
/// </summary>
/// <param name="router">The object that is used to generate the URL.</param>
/// <param name="virtualPath">The generated URL.</param>
/// <param name="dataTokens">The collection of custom values.</param>
public VirtualPathData(
[NotNull] IRouter router,
string virtualPath,
IDictionary<string, object> dataTokens)
{
Router = router;
VirtualPath = virtualPath;
_dataToken = new RouteValueDictionary();
if (dataTokens != null)
{
foreach (var dataToken in dataTokens)
{
_dataToken.Add(dataToken.Key, dataToken.Value);
}
}
}
/// <summary>
/// Gets the collection of custom values for the <see cref="Router"/>.
/// </summary>
public IDictionary<string, object> DataTokens
{
get { return _dataToken; }
}
/// <summary>
/// Gets or sets the <see cref="IRouter"/> that was used to generate the URL.
/// </summary>
public IRouter Router { get; set; }
/// <summary>
/// Gets or sets the URL that was generated from the <see cref="Router"/>.
/// </summary>
public string VirtualPath
{
get
{
return _virtualPath ?? string.Empty;
}
set
{
_virtualPath = value;
}
}
}
}

View File

@ -27,25 +27,27 @@ namespace Microsoft.AspNet.Routing
[InlineData(@"Home/Index/23#Param1=ABC&Param2=Xyz", "Home/Index/23#Param1=ABC&Param2=Xyz", false)]
[InlineData(@"Home/Index/23#Param1=ABC&Param2=Xyz", "home/index/23#Param1=ABC&Param2=Xyz", true)]
public void GetVirtualPath_CanLowerCaseUrls_BasedOnOptions(
string returnUrl,
string lowercaseUrl,
string returnUrl,
string lowercaseUrl,
bool lowercaseUrls)
{
// Arrange
var target = new Mock<IRouter>(MockBehavior.Strict);
target
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Returns(returnUrl);
.Returns(new VirtualPathData(target.Object, returnUrl));
var routeCollection = new RouteCollection();
routeCollection.Add(target.Object);
var virtualPathContext = CreateVirtualPathContext(options: GetRouteOptions(lowercaseUrls));
// Act
var stringVirtualPath = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
// Assert
Assert.Equal(lowercaseUrl, stringVirtualPath);
Assert.Equal(lowercaseUrl, pathData.VirtualPath);
Assert.Same(target.Object, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Theory]
@ -61,17 +63,56 @@ namespace Microsoft.AspNet.Routing
var target = new Mock<IRouter>(MockBehavior.Strict);
target
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Returns(returnUrl);
.Returns(new VirtualPathData(target.Object, returnUrl));
var routeCollection = new RouteCollection();
routeCollection.Add(target.Object);
var virtualPathContext = CreateVirtualPathContext(options: GetRouteOptions(lowercaseUrls));
// Act
var stringVirtualPath = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
// Assert
Assert.Equal(lowercaseUrl, stringVirtualPath);
Assert.Equal(lowercaseUrl, pathData.VirtualPath);
Assert.Same(target.Object, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Theory]
[MemberData("DataTokensTestData")]
public void GetVirtualPath_ReturnsDataTokens(RouteValueDictionary dataTokens, string routerName)
{
// Arrange
var virtualPath = "TestVirtualPath";
var pathContextValues = new RouteValueDictionary { { "controller", virtualPath } };
var pathContext = CreateVirtualPathContext(
pathContextValues,
GetRouteOptions(),
routerName);
var route = CreateTemplateRoute("{controller}", routerName, dataTokens);
var routeCollection = new RouteCollection();
routeCollection.Add(route);
var expectedDataTokens = dataTokens ?? new RouteValueDictionary();
// Act
var pathData = routeCollection.GetVirtualPath(pathContext);
// Assert
Assert.NotNull(pathData);
Assert.Same(route, pathData.Router);
Assert.Equal(virtualPath, pathData.VirtualPath);
Assert.Equal(expectedDataTokens.Count, pathData.DataTokens.Count);
foreach (var dataToken in expectedDataTokens)
{
Assert.True(pathData.DataTokens.ContainsKey(dataToken.Key));
Assert.Equal(dataToken.Value, pathData.DataTokens[dataToken.Key]);
}
}
[Fact]
@ -235,14 +276,17 @@ namespace Microsoft.AspNet.Routing
// Arrange
var routeCollection = GetNestedRouteCollection(new string[] { "Route1", "Route2", "RouteName", "Route3" });
var virtualPathContext = CreateVirtualPathContext(
routeName: "RouteName",
routeName: "RouteName",
options: GetRouteOptions(lowercaseUrls));
// Act
var stringVirtualPath = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
// Assert
Assert.Equal(expectedUrl, stringVirtualPath);
Assert.Equal(expectedUrl, pathData.VirtualPath);
var namedRouter = Assert.IsAssignableFrom<INamedRouter>(pathData.Router);
Assert.Equal(virtualPathContext.RouteName, namedRouter.Name);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -270,10 +314,13 @@ namespace Microsoft.AspNet.Routing
var virtualPathContext = CreateVirtualPathContext(routeName: "Route1", options: GetRouteOptions(true));
// Act
var stringVirtualPath = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
// Assert
Assert.Equal("route1", stringVirtualPath);
Assert.Equal("route1", pathData.VirtualPath);
var namedRouter = Assert.IsAssignableFrom<INamedRouter>(pathData.Router);
Assert.Equal("Route1", namedRouter.Name);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -290,7 +337,7 @@ namespace Microsoft.AspNet.Routing
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => routeCollection.GetVirtualPath(virtualPathContext));
Assert.Equal(
"The supplied route name 'ambiguousRoute' is ambiguous and matched more than one route.",
"The supplied route name 'ambiguousRoute' is ambiguous and matched more than one route.",
ex.Message);
}
@ -341,9 +388,12 @@ namespace Microsoft.AspNet.Routing
var virtualPathContext = CreateVirtualPathContext("Match", options: options);
// Act
var path = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
Assert.Equal("best", path);
Assert.Equal("best", pathData.VirtualPath);
var namedRouter = Assert.IsAssignableFrom<INamedRouter>(pathData.Router);
Assert.Equal("Match", namedRouter.Name);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -368,9 +418,12 @@ namespace Microsoft.AspNet.Routing
var virtualPathContext = CreateVirtualPathContext("Match", options: options);
// Act
var path = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
Assert.Equal("best", path);
Assert.Equal("best", pathData.VirtualPath);
var namedRouter = Assert.IsAssignableFrom<INamedRouter>(pathData.Router);
Assert.Equal("Match", namedRouter.Name);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -395,9 +448,12 @@ namespace Microsoft.AspNet.Routing
var virtualPathContext = CreateVirtualPathContext("Match", options: options);
// Act
var path = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
Assert.Equal("best", path);
Assert.Equal("best", pathData.VirtualPath);
var namedRouter = Assert.IsAssignableFrom<INamedRouter>(pathData.Router);
Assert.Equal("Match", namedRouter.Name);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -421,9 +477,11 @@ namespace Microsoft.AspNet.Routing
var virtualPathContext = CreateVirtualPathContext(options: options);
// Act
var path = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
Assert.Equal("best", path);
Assert.Equal("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());
@ -483,9 +541,11 @@ namespace Microsoft.AspNet.Routing
var virtualPathContext = CreateVirtualPathContext(options: options);
// Act
var path = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
Assert.Equal("best", path);
Assert.Equal("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());
@ -514,9 +574,11 @@ namespace Microsoft.AspNet.Routing
var virtualPathContext = CreateVirtualPathContext(options: options);
// Act
var path = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
Assert.Equal("best", path);
Assert.Equal("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());
@ -545,9 +607,11 @@ namespace Microsoft.AspNet.Routing
var virtualPathContext = CreateVirtualPathContext(options: options);
// Act
var path = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
Assert.Equal("best", path);
Assert.Equal("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());
@ -578,9 +642,11 @@ namespace Microsoft.AspNet.Routing
var virtualPathContext = CreateVirtualPathContext(options: options);
// Act
var path = routeCollection.GetVirtualPath(virtualPathContext);
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
Assert.Equal("best", path);
Assert.Equal("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());
@ -650,11 +716,28 @@ namespace Microsoft.AspNet.Routing
var context = CreateVirtualPathContext(values, options: GetRouteOptions(lowercaseUrls));
// Act
var path = routeCollection.GetVirtualPath(context);
var pathData = routeCollection.GetVirtualPath(context);
// Assert
Assert.True(context.IsBound);
Assert.Equal(expectedUrl, path);
Assert.Equal(expectedUrl, pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
// DataTokens test data for RouterCollection.GetVirtualPath
public static IEnumerable<object[]> DataTokensTestData
{
get
{
yield return new object[] { null, null };
yield return new object[] { new RouteValueDictionary(), null };
yield return new object[] { new RouteValueDictionary() { { "tokenKey", "tokenValue" } }, null };
yield return new object[] { null, "routerA" };
yield return new object[] { new RouteValueDictionary(), "routerA" };
yield return new object[] { new RouteValueDictionary() { { "tokenKey", "tokenValue" } }, "routerA" };
}
}
private static async Task<TestSink> SetUp(bool enabled, bool handled)
@ -730,7 +813,8 @@ namespace Microsoft.AspNet.Routing
target
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(c => c.IsBound = accept && c.RouteName == name)
.Returns<VirtualPathContext>(c => c.RouteName == name ? matchValue : null)
.Returns<VirtualPathContext>(c =>
c.RouteName == name ? new VirtualPathData(target.Object, matchValue) : null)
.Verifiable();
target
@ -746,7 +830,10 @@ namespace Microsoft.AspNet.Routing
return target.Object;
}
private static TemplateRoute CreateTemplateRoute(string template)
private static TemplateRoute CreateTemplateRoute(
string template,
string routerName = null,
RouteValueDictionary dataTokens = null)
{
var target = new Mock<IRouter>(MockBehavior.Strict);
target
@ -755,7 +842,15 @@ namespace Microsoft.AspNet.Routing
.Returns<VirtualPathContext>(rc => null);
var resolverMock = new Mock<IInlineConstraintResolver>();
return new TemplateRoute(target.Object, template, resolverMock.Object);
return new TemplateRoute(
target.Object,
routerName,
template,
defaults: null,
constraints: null,
dataTokens: dataTokens,
inlineConstraintResolver: resolverMock.Object);
}
private static VirtualPathContext CreateVirtualPathContext(
@ -791,7 +886,8 @@ namespace Microsoft.AspNet.Routing
private static VirtualPathContext CreateVirtualPathContext(
RouteValueDictionary values,
RouteOptions options = null)
RouteOptions options = null,
string routeName = null)
{
var optionsAccessor = new Mock<IOptions<RouteOptions>>(MockBehavior.Strict);
optionsAccessor.SetupGet(o => o.Options).Returns(options);
@ -802,8 +898,11 @@ namespace Microsoft.AspNet.Routing
context.Setup(m => m.RequestServices.GetService(typeof(ILoggerFactory)))
.Returns(NullLoggerFactory.Instance);
return new VirtualPathContext(context.Object, null, values);
return new VirtualPathContext(
context.Object,
ambientValues: null,
values: values,
routeName: routeName);
}
private static RouteContext CreateRouteContext(
@ -846,7 +945,7 @@ namespace Microsoft.AspNet.Routing
target
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
.Callback<VirtualPathContext>(c => c.IsBound = accept)
.Returns(accept || match ? matchValue : null)
.Returns(accept || match ? new VirtualPathData(target.Object, matchValue) : null)
.Verifiable();
target

View File

@ -180,9 +180,9 @@ namespace Microsoft.AspNet.Routing
_isHandled = isHandled;
}
public string GetVirtualPath(VirtualPathContext context)
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
return "";
return new VirtualPathData(this, "");
}
public Task RouteAsync(RouteContext context)

View File

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DNX451
@ -563,7 +563,7 @@ namespace Microsoft.AspNet.Routing.Template
// Act
await route.RouteAsync(context);
// Assert
// Assert
Assert.True(context.IsHandled);
Assert.True(routeValues.ContainsKey("ssn"));
Assert.Equal("123-456-7890", routeValues["ssn"]);
@ -949,11 +949,13 @@ namespace Microsoft.AspNet.Routing.Template
var context = CreateVirtualPathContext(new { controller = "Home" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.True(context.IsBound);
Assert.Equal("Home", path);
Assert.Equal("Home", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -971,6 +973,91 @@ namespace Microsoft.AspNet.Routing.Template
Assert.Null(path);
}
[Theory]
[MemberData("DataTokensTestData")]
public void GetVirtualPath_ReturnsDataTokens_WhenTargetReturnsVirtualPathData(
RouteValueDictionary dataTokens)
{
// Arrange
var path = "TestPath";
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 =
new RouteValueDictionary() { { "ThisShouldBeIgnored", "" } };
var route = CreateRoute(
target.Object,
"{controller}",
defaults: null,
dataTokens: routeDataTokens);
var context = CreateVirtualPathContext(new { controller = path });
var expectedDataTokens = dataTokens ?? new RouteValueDictionary();
// Act
var pathData = route.GetVirtualPath(context);
// Assert
Assert.NotNull(pathData);
Assert.Same(target.Object, pathData.Router);
Assert.Equal(path, pathData.VirtualPath);
Assert.NotNull(pathData.DataTokens);
Assert.DoesNotContain(routeDataTokens.First().Key, pathData.DataTokens.Keys);
Assert.Equal(expectedDataTokens.Count, pathData.DataTokens.Count);
foreach (var dataToken in expectedDataTokens)
{
Assert.True(pathData.DataTokens.ContainsKey(dataToken.Key));
Assert.Equal(dataToken.Value, pathData.DataTokens[dataToken.Key]);
}
}
[Theory]
[MemberData("DataTokensTestData")]
public void GetVirtualPath_ReturnsDataTokens_WhenTargetReturnsNullVirtualPathData(
RouteValueDictionary dataTokens)
{
// Arrange
var path = "TestPath";
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(
target.Object,
"{controller}",
defaults: null,
dataTokens: dataTokens);
var context = CreateVirtualPathContext(new { controller = path });
var expectedDataTokens = dataTokens ?? new RouteValueDictionary();
// Act
var pathData = route.GetVirtualPath(context);
// Assert
Assert.NotNull(pathData);
Assert.Same(route, pathData.Router);
Assert.Equal(path, pathData.VirtualPath);
Assert.NotNull(pathData.DataTokens);
Assert.Equal(expectedDataTokens.Count, pathData.DataTokens.Count);
foreach (var dataToken in expectedDataTokens)
{
Assert.True(pathData.DataTokens.ContainsKey(dataToken.Key));
Assert.Equal(dataToken.Value, pathData.DataTokens[dataToken.Key]);
}
}
[Fact]
public void GetVirtualPath_ValuesRejectedByHandler_StillGeneratesPath()
{
@ -979,11 +1066,13 @@ namespace Microsoft.AspNet.Routing.Template
var context = CreateVirtualPathContext(new { controller = "Home" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.False(context.IsBound);
Assert.Equal("Home", path);
Assert.Equal("Home", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -994,11 +1083,13 @@ namespace Microsoft.AspNet.Routing.Template
var context = CreateVirtualPathContext(new { action = "Index" }, new { controller = "Home" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.True(context.IsBound);
Assert.Equal("Home/Index", path);
Assert.Equal("Home/Index", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -1027,19 +1118,21 @@ namespace Microsoft.AspNet.Routing.Template
// Arrange
var context = CreateVirtualPathContext(new { p1 = "hello", p2 = "1234" });
var r = CreateRoute(
var route = CreateRoute(
"{p1}/{p2}",
new { p2 = "catchall" },
true,
new RouteValueDictionary(new { p2 = "\\d{4}" }));
// Act
var virtualPath = r.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.True(context.IsBound);
Assert.NotNull(virtualPath);
Assert.Equal("hello/1234", virtualPath);
Assert.NotNull(pathData);
Assert.Equal("hello/1234", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -1065,23 +1158,24 @@ namespace Microsoft.AspNet.Routing.Template
[Fact]
public void RouteWithCatchAllAcceptsConstraints()
{
// Arrange
// Arrange
var context = CreateVirtualPathContext(new { p1 = "hello", p2 = "1234" });
TemplateRoute r = CreateRoute(
TemplateRoute route = CreateRoute(
"{p1}/{*p2}",
new { p2 = "catchall" },
true,
new RouteValueDictionary(new { p2 = "\\d{4}" }));
// Act
var virtualPath = r.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.True(context.IsBound);
Assert.NotNull(virtualPath);
Assert.Equal("hello/1234", virtualPath);
Assert.NotNull(pathData);
Assert.Equal("hello/1234", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -1099,19 +1193,21 @@ namespace Microsoft.AspNet.Routing.Template
.Returns(true)
.Verifiable();
TemplateRoute r = CreateRoute(
TemplateRoute route = CreateRoute(
"{p1}/{p2}",
new { p2 = "catchall" },
true,
new RouteValueDictionary(new { p2 = target.Object }));
// Act
var virtualPath = r.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.True(context.IsBound);
Assert.NotNull(virtualPath);
Assert.Equal("hello/1234", virtualPath);
Assert.NotNull(pathData);
Assert.Equal("hello/1234", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
target.VerifyAll();
}
@ -1135,10 +1231,13 @@ namespace Microsoft.AspNet.Routing.Template
var expectedValues = new RouteValueDictionary(new { controller = "Home", action = "Store" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Store", path);
Assert.Equal("Home/Store", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
Assert.Equal(expectedValues, childContext.ProvidedValues);
}
@ -1161,10 +1260,13 @@ namespace Microsoft.AspNet.Routing.Template
new { controller = "Home", action = "Store", area = "Admin" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Admin/Home/Store", path);
Assert.Equal("Admin/Home/Store", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
Assert.Equal(expectedValues, childContext.ProvidedValues);
}
@ -1186,10 +1288,13 @@ namespace Microsoft.AspNet.Routing.Template
var expectedValues = new RouteValueDictionary(new { controller = "Home", action = "Store" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Store?id=5", path);
Assert.Equal("Home/Store?id=5", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
Assert.Equal(expectedValues, childContext.ProvidedValues);
}
@ -1214,10 +1319,13 @@ namespace Microsoft.AspNet.Routing.Template
new { controller = "Home", action = "Store", extra = "42" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("slug/Home/Store", path);
Assert.Equal("slug/Home/Store", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
Assert.Equal(expectedValues, constraint.Values);
}
@ -1242,10 +1350,13 @@ namespace Microsoft.AspNet.Routing.Template
new { controller = "Home", action = "Store" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("slug/Home/Store", path);
Assert.Equal("slug/Home/Store", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
Assert.Equal(expectedValues, constraint.Values);
}
@ -1269,10 +1380,13 @@ namespace Microsoft.AspNet.Routing.Template
new { controller = "Shopping", action = "Index" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("slug/Shopping", path);
Assert.Equal("slug/Shopping", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
Assert.Equal(expectedValues, constraint.Values);
}
@ -1297,10 +1411,13 @@ namespace Microsoft.AspNet.Routing.Template
new { controller = "Home", action = "Store", otherthing = "17", thirdthing = "13" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("slug/Home/Store", path);
Assert.Equal("slug/Home/Store", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
Assert.Equal(expectedValues.OrderBy(kvp => kvp.Key), constraint.Values.OrderBy(kvp => kvp.Key));
}
@ -1313,10 +1430,12 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home", id = 4 });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/4", path);
Assert.Equal("Home/Index/4", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -1343,10 +1462,12 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home", id = 98 });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/98", path);
Assert.Equal("Home/Index/98", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -1358,10 +1479,12 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index", path);
Assert.Equal("Home/Index", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -1388,10 +1511,12 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home", id = 14 });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/14", path);
Assert.Equal("Home/Index/14", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
@ -1409,16 +1534,18 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home", name = "products" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/products", path);
Assert.Equal("Home/Index/products", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
public void GetVirtualPath_OptionalParameter_ParameterPresentInValues()
{
// Arrange
// Arrange
var route = CreateRoute(
template: "{controller}/{action}/{name}.{format?}",
defaults: null,
@ -1429,16 +1556,18 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home", name = "products", format = "xml" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/products.xml", path);
Assert.Equal("Home/Index/products.xml", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
public void GetVirtualPath_OptionalParameter_ParameterNotPresentInValues()
{
// Arrange
// Arrange
var route = CreateRoute(
template: "{controller}/{action}/{name}.{format?}",
defaults: null,
@ -1449,16 +1578,18 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home", name = "products" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/products", path);
Assert.Equal("Home/Index/products", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
public void GetVirtualPath_OptionalParameter_ParameterPresentInValuesAndDefaults()
{
// Arrange
// Arrange
var route = CreateRoute(
template: "{controller}/{action}/{name}.{format?}",
defaults: new { format = "json" },
@ -1469,16 +1600,18 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home", name = "products", format = "xml" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/products.xml", path);
Assert.Equal("Home/Index/products.xml", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
public void GetVirtualPath_OptionalParameter_ParameterNotPresentInValues_PresentInDefaults()
{
// Arrange
// Arrange
var route = CreateRoute(
template: "{controller}/{action}/{name}.{format?}",
defaults: new { format = "json" },
@ -1489,16 +1622,18 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home", name = "products" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/products", path);
Assert.Equal("Home/Index/products", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
public void GetVirtualPath_OptionalParameter_ParameterNotPresentInTemplate_PresentInValues()
{
// Arrange
// Arrange
var route = CreateRoute(
template: "{controller}/{action}/{name}",
defaults: null,
@ -1509,16 +1644,18 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home", name = "products", format = "json" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/products?format=json", path);
Assert.Equal("Home/Index/products?format=json", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
public void GetVirtualPath_OptionalParameter_FollowedByDotAfterSlash_ParameterPresent()
{
// Arrange
// Arrange
var route = CreateRoute(
template: "{controller}/{action}/.{name?}",
defaults: null,
@ -1529,16 +1666,18 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home", name = "products" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/.products", path);
Assert.Equal("Home/Index/.products", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
public void GetVirtualPath_OptionalParameter_FollowedByDotAfterSlash_ParameterNotPresent()
{
// Arrange
// Arrange
var route = CreateRoute(
template: "{controller}/{action}/.{name?}",
defaults: null,
@ -1549,16 +1688,18 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index/", path);
Assert.Equal("Home/Index/", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
[Fact]
public void GetVirtualPath_OptionalParameter_InSimpleSegment()
{
// Arrange
// Arrange
var route = CreateRoute(
template: "{controller}/{action}/{name?}",
defaults: null,
@ -1569,10 +1710,12 @@ namespace Microsoft.AspNet.Routing.Template
values: new { action = "Index", controller = "Home" });
// Act
var path = route.GetVirtualPath(context);
var pathData = route.GetVirtualPath(context);
// Assert
Assert.Equal("Home/Index", path);
Assert.Equal("Home/Index", pathData.VirtualPath);
Assert.Same(route, pathData.Router);
Assert.Empty(pathData.DataTokens);
}
private static VirtualPathContext CreateVirtualPathContext(object values)
@ -1771,6 +1914,17 @@ namespace Microsoft.AspNet.Routing.Template
#endregion
// DataTokens test data for TemplateRoute.GetVirtualPath
public static IEnumerable<object[]> DataTokensTestData
{
get
{
yield return new object[] { null };
yield return new object[] { new RouteValueDictionary() };
yield return new object[] { new RouteValueDictionary() { { "tokenKeyA", "tokenValueA" } } };
}
}
private static IRouteBuilder CreateRouteBuilder()
{
var routeBuilder = new RouteBuilder();
@ -1816,13 +1970,17 @@ namespace Microsoft.AspNet.Routing.Template
inlineConstraintResolver: _inlineConstraintResolver);
}
private static TemplateRoute CreateRoute(IRouter target, string template, object defaults)
private static TemplateRoute CreateRoute(
IRouter target,
string template,
object defaults,
RouteValueDictionary dataTokens = null)
{
return new TemplateRoute(target,
template,
new RouteValueDictionary(defaults),
constraints: null,
dataTokens: null,
dataTokens: dataTokens,
inlineConstraintResolver: _inlineConstraintResolver);
}

View File

@ -0,0 +1,67 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if DNX451
using Moq;
using Xunit;
namespace Microsoft.AspNet.Routing
{
public class VirtualPathDataTests
{
[Fact]
public void Constructor_CreatesEmptyDataTokensIfNull()
{
// Arrange
var router = new Mock<IRouter>().Object;
var path = "virtual path";
// Act
var pathData = new VirtualPathData(router, path, null);
// Assert
Assert.Same(router, pathData.Router);
Assert.Same(path, pathData.VirtualPath);
Assert.NotNull(pathData.DataTokens);
Assert.Empty(pathData.DataTokens);
}
[Fact]
public void Constructor_CopiesDataTokens()
{
// Arrange
var router = new Mock<IRouter>().Object;
var path = "virtual path";
var dataTokens = new RouteValueDictionary();
dataTokens["TestKey"] = "TestValue";
// Act
var pathData = new VirtualPathData(router, path, dataTokens);
// Assert
Assert.Same(router, pathData.Router);
Assert.Same(path, pathData.VirtualPath);
Assert.NotNull(pathData.DataTokens);
Assert.Equal("TestValue", pathData.DataTokens["TestKey"]);
Assert.Equal(1, pathData.DataTokens.Count);
Assert.NotSame(dataTokens, pathData.DataTokens);
}
[Fact]
public void VirtualPath_ReturnsEmptyStringIfNull()
{
// Arrange
var router = new Mock<IRouter>().Object;
// Act
var pathData = new VirtualPathData(router, virtualPath: null);
// Assert
Assert.Same(router, pathData.Router);
Assert.Equal(string.Empty, pathData.VirtualPath);
Assert.NotNull(pathData.DataTokens);
Assert.Empty(pathData.DataTokens);
}
}
}
#endif