diff --git a/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs b/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs index 7df2a0e11a..6cd55e201e 100644 --- a/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs +++ b/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs @@ -71,26 +71,30 @@ namespace Microsoft.AspNet.Routing.Template } else { - await _target.RouteAsync(new RouteContext(context.HttpContext){ Values = values }); + // Not currently doing anything to clean this up if it's not a match. Consider hardening this. + context.Values = values; + + await _target.RouteAsync(context); } } public string BindPath(BindPathContext context) { - // Validate that the target can accept these values - if the target generates a value - // then that can short circuit. + // Validate that the target can accept these values. var path = _target.BindPath(context); if (path != null) { + // If the target generates a value then that can short circuit. return path; } else if (!context.IsBound) { + // The target has rejected these values. return null; } // This could be optimized more heavily - right now we try to do the full url - // generation after validating, but we could do it in two phases. + // generation after validating, but we could do it in two phases if the perf is better. path = _binder.Bind(_defaults, context.AmbientValues, context.Values); if (path == null) { diff --git a/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs b/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs new file mode 100644 index 0000000000..c5303b2a03 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs @@ -0,0 +1,110 @@ + +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Routing.Tests +{ + public class RouteCollectionTest + { + [Fact] + public async Task RouteAsync_FirstMatches() + { + // Arrange + var routes = new RouteCollection(); + + var route1 = CreateRoute(accept: true); + routes.Add(route1.Object); + + var route2 = CreateRoute(accept: false); + routes.Add(route2.Object); + + var context = CreateRouteContext("/Cool"); + + // Act + await routes.RouteAsync(context); + + // Assert + route1.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(1)); + route2.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(0)); + Assert.True(context.IsHandled); + } + + [Fact] + public async Task RouteAsync_SecondMatches() + { + // Arrange + var routes = new RouteCollection(); + + var route1 = CreateRoute(accept: false); + routes.Add(route1.Object); + + var route2 = CreateRoute(accept: true); + routes.Add(route2.Object); + + var context = CreateRouteContext("/Cool"); + + // Act + await routes.RouteAsync(context); + + // Assert + route1.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(1)); + route2.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(1)); + Assert.True(context.IsHandled); + } + + [Fact] + public async Task RouteAsync_NoMatch() + { + // Arrange + var routes = new RouteCollection(); + + var route1 = CreateRoute(accept: false); + routes.Add(route1.Object); + + var route2 = CreateRoute(accept: false); + routes.Add(route2.Object); + + var context = CreateRouteContext("/Cool"); + + // Act + await routes.RouteAsync(context); + + // Assert + route1.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(1)); + route2.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(1)); + Assert.False(context.IsHandled); + } + + private static RouteContext CreateRouteContext(string requestPath) + { + var request = new Mock(MockBehavior.Strict); + request.SetupGet(r => r.Path).Returns(new PathString(requestPath)); + + var context = new Mock(MockBehavior.Strict); + context.SetupGet(c => c.Request).Returns(request.Object); + + return new RouteContext(context.Object); + } + + private static Mock CreateRoute(bool accept = true) + { + var target = new Mock(MockBehavior.Strict); + target + .Setup(e => e.BindPath(It.IsAny())) + .Callback(c => c.IsBound = accept) + .Returns(null) + .Verifiable(); + + target + .Setup(e => e.RouteAsync(It.IsAny())) + .Callback(async (c) => c.IsHandled = accept) + .Returns(Task.FromResult(null)) + .Verifiable(); + + + return target; + } + } +}