diff --git a/src/Microsoft.AspNet.Routing/RouteCollection.cs b/src/Microsoft.AspNet.Routing/RouteCollection.cs
index 787188ed0f..d0e34ee99c 100644
--- a/src/Microsoft.AspNet.Routing/RouteCollection.cs
+++ b/src/Microsoft.AspNet.Routing/RouteCollection.cs
@@ -57,10 +57,27 @@ namespace Microsoft.AspNet.Routing
{
var route = this[i];
- await route.RouteAsync(context);
- if (context.IsHandled)
+ var oldRouteData = context.RouteData;
+
+ var newRouteData = new RouteData(oldRouteData);
+ newRouteData.Routers.Add(route);
+
+ try
{
- break;
+ context.RouteData = newRouteData;
+
+ await route.RouteAsync(context);
+ if (context.IsHandled)
+ {
+ break;
+ }
+ }
+ finally
+ {
+ if (!context.IsHandled)
+ {
+ context.RouteData = oldRouteData;
+ }
}
}
diff --git a/src/Microsoft.AspNet.Routing/RouteData.cs b/src/Microsoft.AspNet.Routing/RouteData.cs
index 1d818aaec4..e8992b79f0 100644
--- a/src/Microsoft.AspNet.Routing/RouteData.cs
+++ b/src/Microsoft.AspNet.Routing/RouteData.cs
@@ -1,24 +1,31 @@
// 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;
using System.Collections.Generic;
namespace Microsoft.AspNet.Routing
{
- ///
- /// Summary description for RouteData
- ///
public class RouteData
{
public RouteData()
{
+ DataTokens = new Dictionary(StringComparer.OrdinalIgnoreCase);
Routers = new List();
+ Values = new RouteValueDictionary();
+ }
+
+ public RouteData([NotNull] RouteData other)
+ {
+ DataTokens = new Dictionary(other.DataTokens, StringComparer.OrdinalIgnoreCase);
+ Routers = new List(other.Routers);
+ Values = new RouteValueDictionary(other.Values);
}
public List Routers { get; private set; }
- public IDictionary Values { get; set; }
+ public IDictionary Values { get; private set; }
- public IDictionary DataTokens { get; set; }
+ public IDictionary DataTokens { get; private set; }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs b/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs
index 15d62adc1f..33e88554aa 100644
--- a/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs
+++ b/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs
@@ -120,54 +120,57 @@ namespace Microsoft.AspNet.Routing.Template
// If we got back a null value set, that means the URI did not match
return;
}
- else
+
+ if (!RouteConstraintMatcher.Match(
+ Constraints,
+ values,
+ context.HttpContext,
+ this,
+ RouteDirection.IncomingRequest,
+ _constraintLogger))
{
- var originalValues = context.RouteData.Values;
- context.RouteData.Values = MergeValues(originalValues, values);
-
- try
+ if (_logger.IsEnabled(TraceType.Verbose))
{
- if (RouteConstraintMatcher.Match(Constraints,
- values,
- context.HttpContext,
- this,
- RouteDirection.IncomingRequest,
- _constraintLogger))
- {
- context.RouteData.DataTokens = _dataTokens;
- await _target.RouteAsync(context);
-
- if (_logger.IsEnabled(TraceType.Verbose))
- {
- _logger.WriteValues(CreateRouteAsyncValues(
- requestPath,
- values,
- matchedValues: true,
- matchedConstraints: true,
- handled: context.IsHandled));
- }
- }
- else
- {
- if (_logger.IsEnabled(TraceType.Verbose))
- {
- _logger.WriteValues(CreateRouteAsyncValues(
- requestPath,
- values,
- matchedValues: true,
- matchedConstraints: false,
- handled: context.IsHandled));
- }
- }
+ _logger.WriteValues(CreateRouteAsyncValues(
+ requestPath,
+ values,
+ matchedValues: true,
+ matchedConstraints: false,
+ handled: context.IsHandled));
}
- finally
+
+ return;
+ }
+
+ var oldRouteData = context.RouteData;
+
+ var newRouteData = new RouteData(oldRouteData);
+ MergeValues(newRouteData.DataTokens, _dataTokens);
+ newRouteData.Routers.Add(_target);
+ MergeValues(newRouteData.Values, values);
+
+ try
+ {
+ context.RouteData = newRouteData;
+
+ await _target.RouteAsync(context);
+
+ if (_logger.IsEnabled(TraceType.Verbose))
{
- if (!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;
- }
+ _logger.WriteValues(CreateRouteAsyncValues(
+ requestPath,
+ values,
+ matchedValues: true,
+ matchedConstraints: true,
+ handled: context.IsHandled));
+ }
+ }
+ finally
+ {
+ // Restore the original values to prevent polluting the route data.
+ if (!context.IsHandled)
+ {
+ context.RouteData = oldRouteData;
}
}
}
@@ -304,26 +307,17 @@ namespace Microsoft.AspNet.Routing.Template
return values;
}
- private static IDictionary MergeValues(
- IDictionary originalValues,
+ private static void MergeValues(
+ IDictionary destination,
IDictionary 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;
+ destination[kvp.Key] = kvp.Value;
}
-
- return result;
}
private void EnsureLoggers(HttpContext context)
diff --git a/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs b/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs
index b416ca4384..6179818e1f 100644
--- a/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs
+++ b/test/Microsoft.AspNet.Routing.Tests/RouteCollectionTest.cs
@@ -114,6 +114,9 @@ namespace Microsoft.AspNet.Routing
route1.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(1));
route2.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(0));
Assert.True(context.IsHandled);
+
+ Assert.Equal(1, context.RouteData.Routers.Count);
+ Assert.Same(route1.Object, context.RouteData.Routers[0]);
}
[Fact]
@@ -137,6 +140,9 @@ namespace Microsoft.AspNet.Routing
route1.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(1));
route2.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(1));
Assert.True(context.IsHandled);
+
+ Assert.Equal(1, context.RouteData.Routers.Count);
+ Assert.Same(route2.Object, context.RouteData.Routers[0]);
}
[Fact]
@@ -160,6 +166,8 @@ namespace Microsoft.AspNet.Routing
route1.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(1));
route2.Verify(e => e.RouteAsync(It.IsAny()), Times.Exactly(1));
Assert.False(context.IsHandled);
+
+ Assert.Empty(context.RouteData.Routers);
}
[Fact]
diff --git a/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTest.cs b/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTest.cs
index 49511d9a94..744471f282 100644
--- a/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTest.cs
+++ b/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTest.cs
@@ -175,11 +175,12 @@ namespace Microsoft.AspNet.Routing.Template
var template = "{controller}/{action}/{id:int}";
var context = CreateRouteContext("/Home/Index/5");
- var originalRouteDataValues = new Dictionary
- {
- { "country", "USA" },
- };
- context.RouteData.Values = originalRouteDataValues;
+
+ var originalRouteDataValues = context.RouteData.Values;
+ originalRouteDataValues.Add("country", "USA");
+
+ var originalDataTokens = context.RouteData.DataTokens;
+ originalDataTokens.Add("company", "Contoso");
IDictionary routeValues = null;
var mockTarget = new Mock(MockBehavior.Strict);
@@ -192,7 +193,13 @@ namespace Microsoft.AspNet.Routing.Template
})
.Returns(Task.FromResult(true));
- var route = new TemplateRoute(mockTarget.Object, template, _inlineConstraintResolver);
+ var route = new TemplateRoute(
+ mockTarget.Object,
+ template,
+ defaults: null,
+ constraints: null,
+ dataTokens: new RouteValueDictionary(new { today = "Friday" }),
+ inlineConstraintResolver: _inlineConstraintResolver);
// Act
await route.RouteAsync(context);
@@ -209,8 +216,12 @@ namespace Microsoft.AspNet.Routing.Template
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);
+
+ Assert.Equal("Contoso", context.RouteData.DataTokens["company"]);
+ Assert.Equal("Friday", context.RouteData.DataTokens["today"]);
+ Assert.NotSame(originalDataTokens, context.RouteData.DataTokens);
+ Assert.NotSame(route.DataTokens, context.RouteData.DataTokens);
}
[Fact]
@@ -220,11 +231,11 @@ namespace Microsoft.AspNet.Routing.Template
var template = "{controller}/{action}/{id:int}";
var context = CreateRouteContext("/Home/Index/5");
- var originalRouteDataValues = new Dictionary
- {
- { "country", "USA" },
- };
- context.RouteData.Values = originalRouteDataValues;
+ var originalRouteDataValues = context.RouteData.Values;
+ originalRouteDataValues.Add("country", "USA");
+
+ var originalDataTokens = context.RouteData.DataTokens;
+ originalDataTokens.Add("company", "Contoso");
IDictionary routeValues = null;
var mockTarget = new Mock(MockBehavior.Strict);
@@ -237,7 +248,13 @@ namespace Microsoft.AspNet.Routing.Template
})
.Returns(Task.FromResult(true));
- var route = new TemplateRoute(mockTarget.Object, template, _inlineConstraintResolver);
+ var route = new TemplateRoute(
+ mockTarget.Object,
+ template,
+ defaults: null,
+ constraints: null,
+ dataTokens: new RouteValueDictionary(new { today = "Friday" }),
+ inlineConstraintResolver: _inlineConstraintResolver);
// Act
await route.RouteAsync(context);
@@ -252,8 +269,12 @@ namespace Microsoft.AspNet.Routing.Template
Assert.True(context.RouteData.Values.ContainsKey("country"));
Assert.Equal("USA", context.RouteData.Values["country"]);
-
+ Assert.False(context.RouteData.Values.ContainsKey("id"));
Assert.Same(originalRouteDataValues, context.RouteData.Values);
+
+ Assert.Equal("Contoso", context.RouteData.DataTokens["company"]);
+ Assert.False(context.RouteData.DataTokens.ContainsKey("today"));
+ Assert.Same(originalDataTokens, context.RouteData.DataTokens);
}
[Fact]
@@ -263,11 +284,11 @@ namespace Microsoft.AspNet.Routing.Template
var template = "{controller}/{action}/{id:int}";
var context = CreateRouteContext("/Home/Index/5");
- var originalRouteDataValues = new Dictionary
- {
- { "country", "USA" },
- };
- context.RouteData.Values = originalRouteDataValues;
+ var originalRouteDataValues = context.RouteData.Values;
+ originalRouteDataValues.Add("country", "USA");
+
+ var originalDataTokens = context.RouteData.DataTokens;
+ originalDataTokens.Add("company", "Contoso");
IDictionary routeValues = null;
var mockTarget = new Mock(MockBehavior.Strict);
@@ -280,7 +301,13 @@ namespace Microsoft.AspNet.Routing.Template
})
.Throws(new Exception());
- var route = new TemplateRoute(mockTarget.Object, template, _inlineConstraintResolver);
+ var route = new TemplateRoute(
+ mockTarget.Object,
+ template,
+ defaults: null,
+ constraints: null,
+ dataTokens: new RouteValueDictionary(new { today = "Friday" }),
+ inlineConstraintResolver: _inlineConstraintResolver);
// Act
var ex = await Assert.ThrowsAsync(() => route.RouteAsync(context));
@@ -295,8 +322,12 @@ namespace Microsoft.AspNet.Routing.Template
Assert.True(context.RouteData.Values.ContainsKey("country"));
Assert.Equal("USA", context.RouteData.Values["country"]);
-
+ Assert.False(context.RouteData.Values.ContainsKey("id"));
Assert.Same(originalRouteDataValues, context.RouteData.Values);
+
+ Assert.Equal("Contoso", context.RouteData.DataTokens["company"]);
+ Assert.False(context.RouteData.DataTokens.ContainsKey("today"));
+ Assert.Same(originalDataTokens, context.RouteData.DataTokens);
}
@@ -397,7 +428,39 @@ namespace Microsoft.AspNet.Routing.Template
Assert.False(context.IsHandled);
// Issue #16 tracks this.
- Assert.Null(context.RouteData.Values);
+ Assert.Empty(context.RouteData.Values);
+ }
+
+ [Fact]
+ public async Task Match_RejectedByHandler_ClearsRouters()
+ {
+ // Arrange
+ var route = CreateRoute("{controller}", accept: false);
+ var context = CreateRouteContext("/Home");
+
+ // Act
+ await route.RouteAsync(context);
+
+ // Assert
+ Assert.False(context.IsHandled);
+ Assert.Empty(context.RouteData.Routers);
+ }
+
+ [Fact]
+ public async Task Match_SetsRouters()
+ {
+ // Arrange
+ var target = CreateTarget(accept: true);
+ var route = CreateRoute(target, "{controller}");
+ var context = CreateRouteContext("/Home");
+
+ // Act
+ await route.RouteAsync(context);
+
+ // Assert
+ Assert.True(context.IsHandled);
+ Assert.Equal(1, context.RouteData.Routers.Count);
+ Assert.Same(target, context.RouteData.Routers[0]);
}
[Fact]