Refactor attribute route
This commit is contained in:
parent
064c01cf2b
commit
162c4709c1
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.Core;
|
||||
|
|
@ -103,16 +102,16 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
{
|
||||
Order = routeInfo.Order,
|
||||
Precedence = routeInfo.Precedence,
|
||||
Route = new TemplateRoute(
|
||||
_target,
|
||||
routeInfo.RouteTemplate,
|
||||
defaults: new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
|
||||
Target = _target,
|
||||
RouteName = routeInfo.Name,
|
||||
RouteTemplate = routeInfo.RouteTemplate,
|
||||
TemplateMatcher = new TemplateMatcher(
|
||||
routeInfo.ParsedTemplate,
|
||||
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ AttributeRouting.RouteGroupKey, routeInfo.RouteGroup },
|
||||
},
|
||||
constraints: null,
|
||||
dataTokens: null,
|
||||
inlineConstraintResolver: _constraintResolver),
|
||||
{ AttributeRouting.RouteGroupKey, routeInfo.RouteGroup }
|
||||
}),
|
||||
Constraints = routeInfo.Constraints
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
// 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.AspNet.Routing;
|
||||
using Microsoft.AspNet.Routing.Template;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Routing
|
||||
{
|
||||
|
|
@ -21,9 +24,14 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
/// </summary>
|
||||
public decimal Precedence { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="TemplateRoute"/>.
|
||||
/// </summary>
|
||||
public TemplateRoute Route { get; set; }
|
||||
public IRouter Target { get; set; }
|
||||
|
||||
public string RouteName { get; set; }
|
||||
|
||||
public string RouteTemplate { get; set; }
|
||||
|
||||
public TemplateMatcher TemplateMatcher { get; set; }
|
||||
|
||||
public IReadOnlyDictionary<string, IRouteConstraint> Constraints { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
{
|
||||
private readonly IRouter _next;
|
||||
private readonly LinkGenerationDecisionTree _linkGenerationTree;
|
||||
private readonly TemplateRoute[] _matchingRoutes;
|
||||
private readonly AttributeRouteMatchingEntry[] _matchingEntries;
|
||||
private readonly IDictionary<string, AttributeRouteLinkGenerationEntry> _namedEntries;
|
||||
|
||||
private ILogger _logger;
|
||||
|
|
@ -51,11 +51,10 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
// We use ordinal comparison for the templates because we only care about them being exactly equal and
|
||||
// we don't want to make any equivalence between templates based on the culture of the machine.
|
||||
|
||||
_matchingRoutes = matchingEntries
|
||||
_matchingEntries = matchingEntries
|
||||
.OrderBy(o => o.Order)
|
||||
.ThenBy(e => e.Precedence)
|
||||
.ThenBy(e => e.Route.RouteTemplate, StringComparer.Ordinal)
|
||||
.Select(e => e.Route)
|
||||
.ThenBy(e => e.RouteTemplate, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
var namedEntries = new Dictionary<string, AttributeRouteLinkGenerationEntry>(
|
||||
|
|
@ -101,20 +100,53 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
/// <inheritdoc />
|
||||
public async Task RouteAsync([NotNull] RouteContext context)
|
||||
{
|
||||
foreach (var route in _matchingRoutes)
|
||||
foreach(var matchingEntry in _matchingEntries)
|
||||
{
|
||||
var requestPath = context.HttpContext.Request.Path.Value;
|
||||
|
||||
if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
|
||||
{
|
||||
requestPath = requestPath.Substring(1);
|
||||
}
|
||||
|
||||
var values = matchingEntry.TemplateMatcher.Match(requestPath);
|
||||
if (values == null)
|
||||
{
|
||||
// If we got back a null value set, that means the URI did not match
|
||||
continue;
|
||||
}
|
||||
|
||||
var oldRouteData = context.RouteData;
|
||||
|
||||
var newRouteData = new RouteData(oldRouteData);
|
||||
newRouteData.Routers.Add(route);
|
||||
newRouteData.Routers.Add(matchingEntry.Target);
|
||||
MergeValues(newRouteData.Values, values);
|
||||
|
||||
if (!RouteConstraintMatcher.Match(
|
||||
matchingEntry.Constraints,
|
||||
newRouteData.Values,
|
||||
context.HttpContext,
|
||||
this,
|
||||
RouteDirection.IncomingRequest,
|
||||
_constraintLogger))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
"Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'.",
|
||||
matchingEntry.RouteName,
|
||||
matchingEntry.RouteTemplate);
|
||||
|
||||
try
|
||||
{
|
||||
context.RouteData = newRouteData;
|
||||
await route.RouteAsync(context);
|
||||
|
||||
await matchingEntry.Target.RouteAsync(context);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Restore the original values to prevent polluting the route data.
|
||||
if (!context.IsHandled)
|
||||
{
|
||||
context.RouteData = oldRouteData;
|
||||
|
|
@ -261,6 +293,19 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
return new VirtualPathData(this, path);
|
||||
}
|
||||
|
||||
private static void MergeValues(
|
||||
IDictionary<string, object> destination,
|
||||
IDictionary<string, object> values)
|
||||
{
|
||||
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.
|
||||
destination[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ContextHasSameValue(VirtualPathContext context, string key, object value)
|
||||
{
|
||||
object providedValue;
|
||||
|
|
|
|||
|
|
@ -1459,8 +1459,8 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
Assert.Equal("Index", context.RouteData.Values["action"]);
|
||||
Assert.Single(context.RouteData.Values, kvp => kvp.Key == "test_route_group");
|
||||
|
||||
Assert.IsType<TemplateRoute>(context.RouteData.Routers[0]);
|
||||
Assert.Same(next.Object, context.RouteData.Routers[1]);
|
||||
Assert.Equal(1, context.RouteData.Routers.Count);
|
||||
Assert.Equal(next.Object.GetType(), context.RouteData.Routers[0].GetType());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -1502,8 +1502,8 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
|
||||
Assert.Empty(context.RouteData.Routers);
|
||||
|
||||
Assert.IsType<TemplateRoute>(nestedRouteData.Routers[0]);
|
||||
Assert.Same(next.Object, nestedRouteData.Routers[1]);
|
||||
Assert.Equal(1, nestedRouteData.Routers.Count);
|
||||
Assert.Equal(next.Object.GetType(), nestedRouteData.Routers[0].GetType());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -1545,8 +1545,8 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
|
||||
Assert.Empty(context.RouteData.Routers);
|
||||
|
||||
Assert.IsType<TemplateRoute>(nestedRouteData.Routers[0]);
|
||||
Assert.Same(next.Object, nestedRouteData.Routers[1]);
|
||||
Assert.Equal(1, nestedRouteData.Routers.Count);
|
||||
Assert.Equal(next.Object.GetType(), nestedRouteData.Routers[0].GetType());
|
||||
}
|
||||
|
||||
private static RouteContext CreateRouteContext(string requestPath)
|
||||
|
|
@ -1582,20 +1582,16 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
private static AttributeRouteMatchingEntry CreateMatchingEntry(IRouter router, string template, int order)
|
||||
{
|
||||
var routeGroup = string.Format("{0}&&{1}", order, template);
|
||||
|
||||
var entry = new AttributeRouteMatchingEntry();
|
||||
entry.Route = new TemplateRoute(
|
||||
target: router,
|
||||
routeTemplate: template,
|
||||
defaults: new RouteValueDictionary(new { test_route_group = routeGroup }),
|
||||
constraints: null,
|
||||
dataTokens: null,
|
||||
inlineConstraintResolver: CreateConstraintResolver());
|
||||
|
||||
var routeTemplate = TemplateParser.Parse(template);
|
||||
entry.Precedence = AttributeRoutePrecedence.Compute(routeTemplate);
|
||||
entry.Target = router;
|
||||
entry.RouteTemplate = template;
|
||||
var parsedRouteTemplate = TemplateParser.Parse(template);
|
||||
entry.TemplateMatcher = new TemplateMatcher(
|
||||
parsedRouteTemplate,
|
||||
new RouteValueDictionary(new { test_route_group = routeGroup }));
|
||||
entry.Precedence = AttributeRoutePrecedence.Compute(parsedRouteTemplate);
|
||||
entry.Order = order;
|
||||
|
||||
entry.Constraints = GetRouteConstriants(CreateConstraintResolver(), template, parsedRouteTemplate);
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
|
@ -1661,10 +1657,9 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
It.IsAny<string>()))
|
||||
.Returns(mockConstraint.Object);
|
||||
|
||||
var entry = new AttributeRouteMatchingEntry()
|
||||
{
|
||||
Route = new TemplateRoute(new StubRouter(), template, mockConstraintResolver.Object)
|
||||
};
|
||||
var entry = new AttributeRouteMatchingEntry();
|
||||
entry.Target = new StubRouter();
|
||||
entry.RouteTemplate = template;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
|
@ -1728,7 +1723,9 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
version: 1);
|
||||
}
|
||||
|
||||
private static InnerAttributeRoute CreateRoutingAttributeRoute(ILoggerFactory loggerFactory = null, params AttributeRouteMatchingEntry[] entries)
|
||||
private static InnerAttributeRoute CreateRoutingAttributeRoute(
|
||||
ILoggerFactory loggerFactory = null,
|
||||
params AttributeRouteMatchingEntry[] entries)
|
||||
{
|
||||
loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
|
||||
|
||||
|
|
@ -1741,6 +1738,30 @@ namespace Microsoft.AspNet.Mvc.Routing
|
|||
version: 1);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, IRouteConstraint> GetRouteConstriants(
|
||||
IInlineConstraintResolver inlineConstraintResolver,
|
||||
string template,
|
||||
RouteTemplate parsedRouteTemplate)
|
||||
{
|
||||
var constraintBuilder = new RouteConstraintBuilder(inlineConstraintResolver, template);
|
||||
foreach (var parameter in parsedRouteTemplate.Parameters)
|
||||
{
|
||||
if (parameter.InlineConstraints != null)
|
||||
{
|
||||
if (parameter.IsOptional)
|
||||
{
|
||||
constraintBuilder.SetOptional(parameter.Name);
|
||||
}
|
||||
|
||||
foreach (var constraint in parameter.InlineConstraints)
|
||||
{
|
||||
constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return constraintBuilder.Build();
|
||||
}
|
||||
private class StubRouter : IRouter
|
||||
{
|
||||
public VirtualPathContext GenerationContext { get; set; }
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
{
|
||||
typeof(RouteCollection).FullName,
|
||||
typeof(AttributeRoute).FullName,
|
||||
typeof(TemplateRoute).FullName,
|
||||
typeof(MvcRouteHandler).FullName,
|
||||
},
|
||||
result.Routers);
|
||||
|
|
|
|||
Loading…
Reference in New Issue