[Fixes #90] RouteTemplate does not take RouteData
Changed the implementation of route template to merge the existing route data with the values obtained from parsing the request path with the given template. Restored original route data values in case the route template data does not match.
This commit is contained in:
parent
e0f01aa6bb
commit
02a0a218b9
|
|
@ -121,39 +121,51 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
}
|
||||
else
|
||||
{
|
||||
// Not currently doing anything to clean this up if it's not a match. Consider hardening this.
|
||||
context.RouteData.Values = values;
|
||||
var originalValues = context.RouteData.Values;
|
||||
context.RouteData.Values = MergeValues(originalValues, values);
|
||||
|
||||
if (RouteConstraintMatcher.Match(Constraints,
|
||||
values,
|
||||
context.HttpContext,
|
||||
this,
|
||||
RouteDirection.IncomingRequest,
|
||||
_constraintLogger))
|
||||
try
|
||||
{
|
||||
context.RouteData.DataTokens = _dataTokens;
|
||||
await _target.RouteAsync(context);
|
||||
|
||||
if (_logger.IsEnabled(TraceType.Information))
|
||||
if (RouteConstraintMatcher.Match(Constraints,
|
||||
values,
|
||||
context.HttpContext,
|
||||
this,
|
||||
RouteDirection.IncomingRequest,
|
||||
_constraintLogger))
|
||||
{
|
||||
_logger.WriteValues(CreateRouteAsyncValues(
|
||||
requestPath,
|
||||
values,
|
||||
matchedValues: true,
|
||||
matchedConstraints: true,
|
||||
handled: context.IsHandled));
|
||||
context.RouteData.DataTokens = _dataTokens;
|
||||
await _target.RouteAsync(context);
|
||||
|
||||
if (_logger.IsEnabled(TraceType.Information))
|
||||
{
|
||||
_logger.WriteValues(CreateRouteAsyncValues(
|
||||
requestPath,
|
||||
values,
|
||||
matchedValues: true,
|
||||
matchedConstraints: true,
|
||||
handled: context.IsHandled));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_logger.IsEnabled(TraceType.Information))
|
||||
{
|
||||
_logger.WriteValues(CreateRouteAsyncValues(
|
||||
requestPath,
|
||||
values,
|
||||
matchedValues: true,
|
||||
matchedConstraints: false,
|
||||
handled: context.IsHandled));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
if (_logger.IsEnabled(TraceType.Information))
|
||||
if (!context.IsHandled)
|
||||
{
|
||||
_logger.WriteValues(CreateRouteAsyncValues(
|
||||
requestPath,
|
||||
values,
|
||||
matchedValues: true,
|
||||
matchedConstraints: false,
|
||||
handled: context.IsHandled));
|
||||
// Restore the original route data if we didn't handle the route to prevent
|
||||
// poluting the original route data with the values from this route.
|
||||
context.RouteData.Values = originalValues;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -291,6 +303,28 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
return values;
|
||||
}
|
||||
|
||||
private static IDictionary<string, object> MergeValues(
|
||||
IDictionary<string, object> originalValues,
|
||||
IDictionary<string, object> values)
|
||||
{
|
||||
if (originalValues == null)
|
||||
{
|
||||
// There is nothing to merge
|
||||
return values;
|
||||
}
|
||||
|
||||
var result = new RouteValueDictionary(originalValues);
|
||||
foreach (var kvp in values)
|
||||
{
|
||||
// This will replace the original value for the specified key.
|
||||
// Values from the matched route will take preference over previous
|
||||
// data in the route context.
|
||||
result[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void EnsureLoggers(HttpContext context)
|
||||
{
|
||||
if (_logger == null)
|
||||
|
|
|
|||
|
|
@ -168,6 +168,138 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
Assert.Equal(context.IsHandled, values.Handled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_MergesExistingRouteData_IfRouteMatches()
|
||||
{
|
||||
// Arrange
|
||||
var template = "{controller}/{action}/{id:int}";
|
||||
|
||||
var context = CreateRouteContext("/Home/Index/5");
|
||||
var originalRouteDataValues = new Dictionary<string, object>
|
||||
{
|
||||
{ "country", "USA" },
|
||||
};
|
||||
context.RouteData.Values = originalRouteDataValues;
|
||||
|
||||
IDictionary<string, object> routeValues = null;
|
||||
var mockTarget = new Mock<IRouter>(MockBehavior.Strict);
|
||||
mockTarget
|
||||
.Setup(s => s.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = true;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var route = new TemplateRoute(mockTarget.Object, template, _inlineConstraintResolver);
|
||||
|
||||
// Act
|
||||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(routeValues);
|
||||
|
||||
Assert.True(routeValues.ContainsKey("country"));
|
||||
Assert.Equal("USA", routeValues["country"]);
|
||||
Assert.True(routeValues.ContainsKey("id"));
|
||||
Assert.Equal("5",routeValues["id"]);
|
||||
|
||||
Assert.True(context.RouteData.Values.ContainsKey("country"));
|
||||
Assert.Equal("USA", context.RouteData.Values["country"]);
|
||||
Assert.True(context.RouteData.Values.ContainsKey("id"));
|
||||
Assert.Equal("5", context.RouteData.Values["id"]);
|
||||
|
||||
Assert.NotSame(originalRouteDataValues, context.RouteData.Values);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_CleansUpMergedRouteData_IfRouteDoesNotMatch()
|
||||
{
|
||||
// Arrange
|
||||
var template = "{controller}/{action}/{id:int}";
|
||||
|
||||
var context = CreateRouteContext("/Home/Index/5");
|
||||
var originalRouteDataValues = new Dictionary<string, object>
|
||||
{
|
||||
{ "country", "USA" },
|
||||
};
|
||||
context.RouteData.Values = originalRouteDataValues;
|
||||
|
||||
IDictionary<string, object> routeValues = null;
|
||||
var mockTarget = new Mock<IRouter>(MockBehavior.Strict);
|
||||
mockTarget
|
||||
.Setup(s => s.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = false;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var route = new TemplateRoute(mockTarget.Object, template, _inlineConstraintResolver);
|
||||
|
||||
// Act
|
||||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(routeValues);
|
||||
|
||||
Assert.True(routeValues.ContainsKey("country"));
|
||||
Assert.Equal("USA", routeValues["country"]);
|
||||
Assert.True(routeValues.ContainsKey("id"));
|
||||
Assert.Equal("5", routeValues["id"]);
|
||||
|
||||
Assert.True(context.RouteData.Values.ContainsKey("country"));
|
||||
Assert.Equal("USA", context.RouteData.Values["country"]);
|
||||
|
||||
Assert.Same(originalRouteDataValues, context.RouteData.Values);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_CleansUpMergedRouteData_IfHandlerThrows()
|
||||
{
|
||||
// Arrange
|
||||
var template = "{controller}/{action}/{id:int}";
|
||||
|
||||
var context = CreateRouteContext("/Home/Index/5");
|
||||
var originalRouteDataValues = new Dictionary<string, object>
|
||||
{
|
||||
{ "country", "USA" },
|
||||
};
|
||||
context.RouteData.Values = originalRouteDataValues;
|
||||
|
||||
IDictionary<string, object> routeValues = null;
|
||||
var mockTarget = new Mock<IRouter>(MockBehavior.Strict);
|
||||
mockTarget
|
||||
.Setup(s => s.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = false;
|
||||
})
|
||||
.Throws(new Exception());
|
||||
|
||||
var route = new TemplateRoute(mockTarget.Object, template, _inlineConstraintResolver);
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<Exception>(() => route.RouteAsync(context));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(routeValues);
|
||||
|
||||
Assert.True(routeValues.ContainsKey("country"));
|
||||
Assert.Equal("USA", routeValues["country"]);
|
||||
Assert.True(routeValues.ContainsKey("id"));
|
||||
Assert.Equal("5", routeValues["id"]);
|
||||
|
||||
Assert.True(context.RouteData.Values.ContainsKey("country"));
|
||||
Assert.Equal("USA", context.RouteData.Values["country"]);
|
||||
|
||||
Assert.Same(originalRouteDataValues, context.RouteData.Values);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_MatchFailOnConstraints_DoesNotLogWhenDisabled()
|
||||
{
|
||||
|
|
@ -265,7 +397,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
Assert.False(context.IsHandled);
|
||||
|
||||
// Issue #16 tracks this.
|
||||
Assert.NotNull(context.RouteData.Values);
|
||||
Assert.Null(context.RouteData.Values);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -490,9 +622,9 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
.Returns<string>(null);
|
||||
|
||||
var route = CreateRoute(target.Object, "{controller}/{action}");
|
||||
var context = CreateVirtualPathContext(new { action = "Store" }, new { Controller = "Home", action = "Blog"});
|
||||
var context = CreateVirtualPathContext(new { action = "Store" }, new { Controller = "Home", action = "Blog" });
|
||||
|
||||
var expectedValues = new RouteValueDictionary(new {controller = "Home", action = "Store"});
|
||||
var expectedValues = new RouteValueDictionary(new { controller = "Home", action = "Store" });
|
||||
|
||||
// Act
|
||||
var path = route.GetVirtualPath(context);
|
||||
|
|
@ -513,7 +645,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
.Callback<VirtualPathContext>(c => { childContext = c; c.IsBound = true; })
|
||||
.Returns<string>(null);
|
||||
|
||||
var route = CreateRoute(target.Object, "Admin/{controller}/{action}", new {area = "Admin"});
|
||||
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" });
|
||||
|
|
@ -564,7 +696,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
constraints: new { c = constraint });
|
||||
|
||||
var context = CreateVirtualPathContext(
|
||||
values: new { action = "Store" },
|
||||
values: new { action = "Store" },
|
||||
ambientValues: new { Controller = "Home", action = "Blog", extra = "42" });
|
||||
|
||||
var expectedValues = new RouteValueDictionary(
|
||||
|
|
@ -750,7 +882,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
var routeBuilder = CreateRouteBuilder();
|
||||
|
||||
// Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(() => routeBuilder.MapRoute("mockName",
|
||||
ExceptionAssert.Throws<InvalidOperationException>(() => routeBuilder.MapRoute("mockName",
|
||||
"{controller}/{action}",
|
||||
defaults: null,
|
||||
constraints: new { controller = "a.*", action = new Object() }),
|
||||
|
|
@ -937,10 +1069,10 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
public IDictionary<string, object> Values { get; private set; }
|
||||
|
||||
public bool Match(
|
||||
HttpContext httpContext,
|
||||
IRouter route,
|
||||
string routeKey,
|
||||
IDictionary<string, object> values,
|
||||
HttpContext httpContext,
|
||||
IRouter route,
|
||||
string routeKey,
|
||||
IDictionary<string, object> values,
|
||||
RouteDirection routeDirection)
|
||||
{
|
||||
Values = new RouteValueDictionary(values);
|
||||
|
|
|
|||
Loading…
Reference in New Issue