From 87704c67e1c2e1f89fef04330fa043cbfb2eb633 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Wed, 20 Apr 2016 17:04:01 -0700 Subject: [PATCH] React to routing cleanup --- .../Internal/AttributeRoute.cs | 182 ++++------------- .../Internal/AttributeRouteEntries.cs | 4 +- .../Internal/AttributeRouting.cs | 18 +- .../Internal/AttributeRouteTest.cs | 191 ++++++++---------- .../Internal/AttributeRoutingTest.cs | 3 + 5 files changed, 129 insertions(+), 269 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRoute.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRoute.cs index 8ddd32aefe..fcec2c2e86 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRoute.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRoute.cs @@ -4,43 +4,34 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Core; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.Tree; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.ObjectPool; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Mvc.Internal { public class AttributeRoute : IRouter { - private readonly IRouter _target; + private readonly IRouter _handler; private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider; - private readonly IInlineConstraintResolver _constraintResolver; - private readonly ObjectPool _contextPool; - private readonly UrlEncoder _urlEncoder; - private readonly ILoggerFactory _loggerFactory; + private readonly IServiceProvider _services; private TreeRouter _router; public AttributeRoute( - IRouter target, + IRouter handler, IActionDescriptorCollectionProvider actionDescriptorCollectionProvider, - IInlineConstraintResolver constraintResolver, - ObjectPool contextPool, - UrlEncoder urlEncoder, - ILoggerFactory loggerFactory) + IServiceProvider services) { - if (target == null) + if (handler == null) { - throw new ArgumentNullException(nameof(target)); + throw new ArgumentNullException(nameof(handler)); } if (actionDescriptorCollectionProvider == null) @@ -48,32 +39,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal throw new ArgumentNullException(nameof(actionDescriptorCollectionProvider)); } - if (constraintResolver == null) + if (services == null) { - throw new ArgumentNullException(nameof(constraintResolver)); + throw new ArgumentNullException(nameof(services)); } - if (contextPool == null) - { - throw new ArgumentNullException(nameof(contextPool)); - } - - if (urlEncoder == null) - { - throw new ArgumentNullException(nameof(urlEncoder)); - } - - if (loggerFactory == null) - { - throw new ArgumentNullException(nameof(loggerFactory)); - } - - _target = target; + _handler = handler; _actionDescriptorCollectionProvider = actionDescriptorCollectionProvider; - _constraintResolver = constraintResolver; - _contextPool = contextPool; - _urlEncoder = urlEncoder; - _loggerFactory = loggerFactory; + _services = services; } /// @@ -98,38 +71,29 @@ namespace Microsoft.AspNetCore.Mvc.Internal // it on startup. if (_router == null || _router.Version != actions.Version) { - var entries = GetEntries(actions); - _router = BuildRoute(entries, actions.Version); + var builder = _services.GetRequiredService(); + AddEntries(builder, actions); + _router = builder.Build(actions.Version); } return _router; } // internal for testing - internal AttributeRouteEntries GetEntries(ActionDescriptorCollection actions) + internal void AddEntries(TreeRouteBuilder builder, ActionDescriptorCollection actions) { - var entries = new AttributeRouteEntries(); - - var routeInfos = GetRouteInfos(_constraintResolver, actions.Items); + var routeInfos = GetRouteInfos(actions.Items); // We're creating one TreeRouteLinkGenerationEntry per action. This allows us to match the intended // action by expected route values, and then use the TemplateBinder to generate the link. foreach (var routeInfo in routeInfos) { - entries.LinkGenerationEntries.Add(new TreeRouteLinkGenerationEntry() - { - // Using routeInfo.Defaults here WITHOUT adding the RouteGroupKey. We don't want to impact the - // behavior of link generation. - Binder = new TemplateBinder(_urlEncoder, _contextPool, routeInfo.ParsedTemplate, routeInfo.Defaults), - Defaults = routeInfo.Defaults, - Constraints = routeInfo.Constraints, - Order = routeInfo.Order, - GenerationPrecedence = routeInfo.GenerationPrecedence, - RequiredLinkValues = routeInfo.ActionDescriptor.RouteValueDefaults, - RouteGroup = routeInfo.RouteGroup, - Template = routeInfo.ParsedTemplate, - Name = routeInfo.Name, - }); + builder.MapOutbound( + _handler, + routeInfo.RouteTemplate, + new RouteValueDictionary(routeInfo.ActionDescriptor.RouteValueDefaults), + routeInfo.RouteName, + routeInfo.Order); } // We're creating one AttributeRouteMatchingEntry per group, so we need to identify the distinct set of @@ -143,39 +107,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal // // We then inject the route group as a default for the matcher so it gets passed back to MVC // for use in action selection. - var defaults = new RouteValueDictionary(routeInfo.Defaults); - defaults[TreeRouter.RouteGroupKey] = routeInfo.RouteGroup; + var entry = builder.MapInbound( + _handler, + routeInfo.RouteTemplate, + routeInfo.RouteName, + routeInfo.Order); - entries.MatchingEntries.Add(new TreeRouteMatchingEntry() - { - Order = routeInfo.Order, - Precedence = routeInfo.MatchPrecedence, - Target = _target, - RouteName = routeInfo.Name, - RouteTemplate = routeInfo.ParsedTemplate, - TemplateMatcher = new TemplateMatcher(routeInfo.ParsedTemplate, defaults), - Constraints = routeInfo.Constraints, - }); + entry.Defaults[TreeRouter.RouteGroupKey] = routeInfo.RouteGroup; } - - return entries; - } - - private TreeRouter BuildRoute(AttributeRouteEntries entries, int version) - { - var routeBuilder = new TreeRouteBuilder(_target, _loggerFactory); - - foreach (var entry in entries.LinkGenerationEntries) - { - routeBuilder.Add(entry); - } - - foreach (var entry in entries.MatchingEntries) - { - routeBuilder.Add(entry); - } - - return routeBuilder.Build(version); } private static IEnumerable GroupRouteInfosByGroupId(List routeInfos) @@ -193,9 +132,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal return routeInfosByGroupId.Values; } - private static List GetRouteInfos( - IInlineConstraintResolver constraintResolver, - IReadOnlyList actions) + private static List GetRouteInfos(IReadOnlyList actions) { var routeInfos = new List(); var errors = new List(); @@ -210,7 +147,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var attributeRoutedActions = actions.Where(a => a.AttributeRouteInfo?.Template != null); foreach (var action in attributeRoutedActions) { - var routeInfo = GetRouteInfo(constraintResolver, templateCache, action); + var routeInfo = GetRouteInfo(templateCache, action); if (routeInfo.ErrorMessage == null) { routeInfos.Add(routeInfo); @@ -239,7 +176,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal } private static RouteInfo GetRouteInfo( - IInlineConstraintResolver constraintResolver, Dictionary templateCache, ActionDescriptor action) { @@ -261,7 +197,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal { ActionDescriptor = action, RouteGroup = constraint.RouteValue, - RouteTemplate = action.AttributeRouteInfo.Template, }; try @@ -274,7 +209,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal templateCache.Add(action.AttributeRouteInfo.Template, parsedTemplate); } - routeInfo.ParsedTemplate = parsedTemplate; + routeInfo.RouteTemplate = parsedTemplate; } catch (Exception ex) { @@ -284,12 +219,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal foreach (var kvp in action.RouteValueDefaults) { - foreach (var parameter in routeInfo.ParsedTemplate.Parameters) + foreach (var parameter in routeInfo.RouteTemplate.Parameters) { if (string.Equals(kvp.Key, parameter.Name, StringComparison.OrdinalIgnoreCase)) { routeInfo.ErrorMessage = Resources.FormatAttributeRoute_CannotContainParameter( - routeInfo.RouteTemplate, + routeInfo.RouteTemplate.TemplateText, kvp.Key, kvp.Value); @@ -299,40 +234,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal } routeInfo.Order = action.AttributeRouteInfo.Order; - - routeInfo.MatchPrecedence = RoutePrecedence.ComputeMatched(routeInfo.ParsedTemplate); - routeInfo.GenerationPrecedence = RoutePrecedence.ComputeGenerated(routeInfo.ParsedTemplate); - - routeInfo.Name = action.AttributeRouteInfo.Name; - - var constraintBuilder = new RouteConstraintBuilder(constraintResolver, routeInfo.RouteTemplate); - - foreach (var parameter in routeInfo.ParsedTemplate.Parameters) - { - if (parameter.InlineConstraints != null) - { - if (parameter.IsOptional) - { - constraintBuilder.SetOptional(parameter.Name); - } - - foreach (var inlineConstraint in parameter.InlineConstraints) - { - constraintBuilder.AddResolvedConstraint(parameter.Name, inlineConstraint.Constraint); - } - } - } - - routeInfo.Constraints = constraintBuilder.Build(); - - routeInfo.Defaults = new RouteValueDictionary(); - foreach (var parameter in routeInfo.ParsedTemplate.Parameters) - { - if (parameter.DefaultValue != null) - { - routeInfo.Defaults.Add(parameter.Name, parameter.DefaultValue); - } - } + routeInfo.RouteName = action.AttributeRouteInfo.Name; return routeInfo; } @@ -341,25 +243,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal { public ActionDescriptor ActionDescriptor { get; set; } - public IDictionary Constraints { get; set; } - - public RouteValueDictionary Defaults { get; set; } - public string ErrorMessage { get; set; } - - public RouteTemplate ParsedTemplate { get; set; } - + public int Order { get; set; } - public decimal MatchPrecedence { get; set; } - - public decimal GenerationPrecedence { get; set; } - public string RouteGroup { get; set; } - public string RouteTemplate { get; set; } + public string RouteName { get; set; } - public string Name { get; set; } + public RouteTemplate RouteTemplate { get; set; } } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRouteEntries.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRouteEntries.cs index d1acc42b72..735e7bfcf8 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRouteEntries.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRouteEntries.cs @@ -8,8 +8,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal { public class AttributeRouteEntries { - public List LinkGenerationEntries { get; } = new List(); + public List InboundEntries { get; } = new List(); - public List MatchingEntries { get; } = new List(); + public List OutboundEntries { get; } = new List(); } } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRouting.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRouting.cs index e6f842a55c..9f21e66105 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRouting.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/AttributeRouting.cs @@ -2,14 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Text.Encodings.Web; using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Internal; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.ObjectPool; namespace Microsoft.AspNetCore.Mvc.Internal { @@ -33,19 +28,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal throw new ArgumentNullException(nameof(services)); } - var actionDescriptorProvider = services.GetRequiredService(); - var inlineConstraintResolver = services.GetRequiredService(); - var pool = services.GetRequiredService>(); - var urlEncoder = services.GetRequiredService(); - var loggerFactory = services.GetRequiredService(); - return new AttributeRoute( target, - actionDescriptorProvider, - inlineConstraintResolver, - pool, - urlEncoder, - loggerFactory); + services.GetRequiredService(), + services); } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRouteTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRouteTest.cs index d05ac8019f..305c737e57 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRouteTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRouteTest.cs @@ -10,14 +10,12 @@ using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Constraints; -using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Template; using Microsoft.AspNetCore.Routing.Tree; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.ObjectPool; -using Microsoft.Extensions.WebEncoders.Testing; using Moq; using Xunit; @@ -105,7 +103,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal } [Fact] - public void AttributeRoute_GetEntries_CreatesLinkGenerationEntry() + public void AttributeRoute_GetEntries_CreatesOutboundEntry() { // Arrange var actions = new List() @@ -130,32 +128,30 @@ namespace Microsoft.AspNetCore.Mvc.Internal }, }; + var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); - var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act - var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); + route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( - entries.LinkGenerationEntries, + builder.OutboundEntries, e => { - Assert.NotNull(e.Binder); Assert.Empty(e.Constraints); Assert.Empty(e.Defaults); - Assert.Equal(RoutePrecedence.ComputeGenerated(e.Template), e.GenerationPrecedence); - Assert.Equal("BLOG_INDEX", e.Name); + Assert.Equal(RoutePrecedence.ComputeOutbound(e.RouteTemplate), e.Precedence); + Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal(17, e.Order); - Assert.Equal(actions[0].RouteValueDefaults, e.RequiredLinkValues); - Assert.Equal("1", e.RouteGroup); - Assert.Equal("api/Blog/{id}", e.Template.TemplateText); + Assert.Equal(actions[0].RouteValueDefaults.ToArray(), e.RequiredLinkValues.ToArray()); + Assert.Equal("api/Blog/{id}", e.RouteTemplate.TemplateText); }); } [Fact] - public void AttributeRoute_GetEntries_CreatesLinkGenerationEntry_WithConstraint() + public void AttributeRoute_GetEntries_CreatesOutboundEntry_WithConstraint() { // Arrange var actions = new List() @@ -180,32 +176,30 @@ namespace Microsoft.AspNetCore.Mvc.Internal }, }; + var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); - var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act - var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); + route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( - entries.LinkGenerationEntries, + builder.OutboundEntries, e => { - Assert.NotNull(e.Binder); Assert.Single(e.Constraints, kvp => kvp.Key == "id"); Assert.Empty(e.Defaults); - Assert.Equal(RoutePrecedence.ComputeGenerated(e.Template), e.GenerationPrecedence); - Assert.Equal("BLOG_INDEX", e.Name); + Assert.Equal(RoutePrecedence.ComputeOutbound(e.RouteTemplate), e.Precedence); + Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal(17, e.Order); - Assert.Equal(actions[0].RouteValueDefaults, e.RequiredLinkValues); - Assert.Equal("1", e.RouteGroup); - Assert.Equal("api/Blog/{id:int}", e.Template.TemplateText); + Assert.Equal(actions[0].RouteValueDefaults.ToArray(), e.RequiredLinkValues.ToArray()); + Assert.Equal("api/Blog/{id:int}", e.RouteTemplate.TemplateText); }); } [Fact] - public void AttributeRoute_GetEntries_CreatesLinkGenerationEntry_WithDefault() + public void AttributeRoute_GetEntries_CreatesOutboundEntry_WithDefault() { // Arrange var actions = new List() @@ -230,27 +224,25 @@ namespace Microsoft.AspNetCore.Mvc.Internal }, }; + var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); - var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act - var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); + route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( - entries.LinkGenerationEntries, + builder.OutboundEntries, e => { - Assert.NotNull(e.Binder); Assert.Empty(e.Constraints); Assert.Equal(new RouteValueDictionary(new { slug = "hello" }), e.Defaults); - Assert.Equal(RoutePrecedence.ComputeGenerated(e.Template), e.GenerationPrecedence); - Assert.Equal("BLOG_INDEX", e.Name); + Assert.Equal(RoutePrecedence.ComputeOutbound(e.RouteTemplate), e.Precedence); + Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal(17, e.Order); - Assert.Equal(actions[0].RouteValueDefaults, e.RequiredLinkValues); - Assert.Equal("1", e.RouteGroup); - Assert.Equal("api/Blog/{*slug=hello}", e.Template.TemplateText); + Assert.Equal(actions[0].RouteValueDefaults.ToArray(), e.RequiredLinkValues.ToArray()); + Assert.Equal("api/Blog/{*slug=hello}", e.RouteTemplate.TemplateText); }); } @@ -258,7 +250,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal // actions define the same route info. Link generation happens based on the action name + controller // name. [Fact] - public void AttributeRoute_GetEntries_CreatesLinkGenerationEntry_ForEachAction() + public void AttributeRoute_GetEntries_CreatesOutboundEntry_ForEachAction() { // Arrange var actions = new List() @@ -301,44 +293,40 @@ namespace Microsoft.AspNetCore.Mvc.Internal }, }; + var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); - var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act - var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); + route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( - entries.LinkGenerationEntries, + builder.OutboundEntries, e => { - Assert.NotNull(e.Binder); Assert.Empty(e.Constraints); Assert.Empty(e.Defaults); - Assert.Equal(RoutePrecedence.ComputeGenerated(e.Template), e.GenerationPrecedence); - Assert.Equal("BLOG_INDEX", e.Name); + Assert.Equal(RoutePrecedence.ComputeOutbound(e.RouteTemplate), e.Precedence); + Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal(17, e.Order); - Assert.Equal(actions[0].RouteValueDefaults, e.RequiredLinkValues); - Assert.Equal("1", e.RouteGroup); - Assert.Equal("api/Blog/{id}", e.Template.TemplateText); + Assert.Equal(actions[0].RouteValueDefaults.ToArray(), e.RequiredLinkValues.ToArray()); + Assert.Equal("api/Blog/{id}", e.RouteTemplate.TemplateText); }, e => { - Assert.NotNull(e.Binder); Assert.Empty(e.Constraints); Assert.Empty(e.Defaults); - Assert.Equal(RoutePrecedence.ComputeGenerated(e.Template), e.GenerationPrecedence); - Assert.Equal("BLOG_INDEX", e.Name); + Assert.Equal(RoutePrecedence.ComputeOutbound(e.RouteTemplate), e.Precedence); + Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal(17, e.Order); - Assert.Equal(actions[1].RouteValueDefaults, e.RequiredLinkValues); - Assert.Equal("1", e.RouteGroup); - Assert.Equal("api/Blog/{id}", e.Template.TemplateText); + Assert.Equal(actions[1].RouteValueDefaults.ToArray(), e.RequiredLinkValues.ToArray()); + Assert.Equal("api/Blog/{id}", e.RouteTemplate.TemplateText); }); } [Fact] - public void AttributeRoute_GetEntries_CreatesMatchingEntry() + public void AttributeRoute_GetEntries_CreatesInboundEntry() { // Arrange var actions = new List() @@ -363,34 +351,31 @@ namespace Microsoft.AspNetCore.Mvc.Internal }, }; + var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); - - var handler = CreateHandler().Object; - var route = CreateRoute(handler, actionDescriptorProvider.Object); + var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act - var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); + route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( - entries.MatchingEntries, + builder.InboundEntries, e => { Assert.Empty(e.Constraints); Assert.Equal(17, e.Order); - Assert.Equal(RoutePrecedence.ComputeMatched(e.RouteTemplate), e.Precedence); + Assert.Equal(RoutePrecedence.ComputeInbound(e.RouteTemplate), e.Precedence); Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal("api/Blog/{id}", e.RouteTemplate.TemplateText); - Assert.Same(handler, e.Target); Assert.Collection( - e.TemplateMatcher.Defaults.OrderBy(kvp => kvp.Key), + e.Defaults.OrderBy(kvp => kvp.Key), kvp => Assert.Equal(new KeyValuePair(TreeRouter.RouteGroupKey, "1"), kvp)); - Assert.Same(e.RouteTemplate, e.TemplateMatcher.Template); }); } [Fact] - public void AttributeRoute_GetEntries_CreatesMatchingEntry_WithConstraint() + public void AttributeRoute_GetEntries_CreatesInboundEntry_WithConstraint() { // Arrange var actions = new List() @@ -415,34 +400,31 @@ namespace Microsoft.AspNetCore.Mvc.Internal }, }; + var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); - - var handler = CreateHandler().Object; - var route = CreateRoute(handler, actionDescriptorProvider.Object); + var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act - var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); + route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( - entries.MatchingEntries, + builder.InboundEntries, e => { Assert.Single(e.Constraints, kvp => kvp.Key == "id"); Assert.Equal(17, e.Order); - Assert.Equal(RoutePrecedence.ComputeMatched(e.RouteTemplate), e.Precedence); + Assert.Equal(RoutePrecedence.ComputeInbound(e.RouteTemplate), e.Precedence); Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal("api/Blog/{id:int}", e.RouteTemplate.TemplateText); - Assert.Same(handler, e.Target); Assert.Collection( - e.TemplateMatcher.Defaults.OrderBy(kvp => kvp.Key), + e.Defaults.OrderBy(kvp => kvp.Key), kvp => Assert.Equal(new KeyValuePair(TreeRouter.RouteGroupKey, "1"), kvp)); - Assert.Same(e.RouteTemplate, e.TemplateMatcher.Template); }); } [Fact] - public void AttributeRoute_GetEntries_CreatesMatchingEntry_WithDefault() + public void AttributeRoute_GetEntries_CreatesInboundEntry_WithDefault() { // Arrange var actions = new List() @@ -467,30 +449,27 @@ namespace Microsoft.AspNetCore.Mvc.Internal }, }; + var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); - - var handler = CreateHandler().Object; - var route = CreateRoute(handler, actionDescriptorProvider.Object); + var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act - var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); + route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( - entries.MatchingEntries, + builder.InboundEntries, e => { Assert.Empty(e.Constraints); Assert.Equal(17, e.Order); - Assert.Equal(RoutePrecedence.ComputeMatched(e.RouteTemplate), e.Precedence); + Assert.Equal(RoutePrecedence.ComputeInbound(e.RouteTemplate), e.Precedence); Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal("api/Blog/{*slug=hello}", e.RouteTemplate.TemplateText); - Assert.Same(handler, e.Target); Assert.Collection( - e.TemplateMatcher.Defaults.OrderBy(kvp => kvp.Key), + e.Defaults.OrderBy(kvp => kvp.Key), kvp => Assert.Equal(new KeyValuePair(TreeRouter.RouteGroupKey, "1"), kvp), kvp => Assert.Equal(new KeyValuePair("slug", "hello"), kvp)); - Assert.Same(e.RouteTemplate, e.TemplateMatcher.Template); }); } @@ -498,7 +477,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal // actions define the same route info. Link generation happens based on the action name + controller // name. [Fact] - public void AttributeRoute_GetEntries_CreatesMatchingEntry_CombinesLikeActions() + public void AttributeRoute_GetEntries_CreatesInboundEntry_CombinesLikeActions() { // Arrange var actions = new List() @@ -541,32 +520,41 @@ namespace Microsoft.AspNetCore.Mvc.Internal }, }; + var builder = CreateBuilder(); var actionDescriptorProvider = CreateActionDescriptorProvider(actions); - - var handler = CreateHandler().Object; - var route = CreateRoute(handler, actionDescriptorProvider.Object); + var route = CreateRoute(CreateHandler().Object, actionDescriptorProvider.Object); // Act - var entries = route.GetEntries(actionDescriptorProvider.Object.ActionDescriptors); + route.AddEntries(builder, actionDescriptorProvider.Object.ActionDescriptors); // Assert Assert.Collection( - entries.MatchingEntries, + builder.InboundEntries, e => { Assert.Empty(e.Constraints); Assert.Equal(17, e.Order); - Assert.Equal(RoutePrecedence.ComputeMatched(e.RouteTemplate), e.Precedence); + Assert.Equal(RoutePrecedence.ComputeInbound(e.RouteTemplate), e.Precedence); Assert.Equal("BLOG_INDEX", e.RouteName); Assert.Equal("api/Blog/{id}", e.RouteTemplate.TemplateText); - Assert.Same(handler, e.Target); Assert.Collection( - e.TemplateMatcher.Defaults.OrderBy(kvp => kvp.Key), + e.Defaults.OrderBy(kvp => kvp.Key), kvp => Assert.Equal(new KeyValuePair(TreeRouter.RouteGroupKey, "1"), kvp)); - Assert.Same(e.RouteTemplate, e.TemplateMatcher.Template); }); } + private static TreeRouteBuilder CreateBuilder() + { + var services = new ServiceCollection() + .AddSingleton() + .AddSingleton(NullLoggerFactory.Instance) + .AddLogging() + .AddRouting() + .AddOptions() + .BuildServiceProvider(); + return services.GetRequiredService(); + } + private static Mock CreateHandler() { var handler = new Mock(MockBehavior.Strict); @@ -593,23 +581,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal IRouter handler, IActionDescriptorCollectionProvider actionDescriptorProvider) { - var constraintResolver = new Mock(); - constraintResolver - .Setup(c => c.ResolveConstraint("int")) - .Returns(new IntRouteConstraint()); - - var policy = new UriBuilderContextPooledObjectPolicy(new UrlTestEncoder()); - var pool = new DefaultObjectPool(policy); - - var route = new AttributeRoute( - handler, - actionDescriptorProvider, - constraintResolver.Object, - pool, - new UrlTestEncoder(), - NullLoggerFactory.Instance); - - return route; + var services = new ServiceCollection() + .AddSingleton() + .AddSingleton(NullLoggerFactory.Instance) + .AddLogging() + .AddRouting() + .AddOptions() + .BuildServiceProvider(); + return new AttributeRoute(handler, actionDescriptorProvider, services); } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRoutingTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRoutingTest.cs index 2fd6b154fe..9143611aac 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRoutingTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/AttributeRoutingTest.cs @@ -199,7 +199,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal .AddSingleton() .AddSingleton(new UrlTestEncoder()); + services.AddSingleton(); services.AddRouting(); + services.AddOptions(); + services.AddLogging(); return services.AddSingleton(actionDescriptorProvider.Object) .AddSingleton(NullLoggerFactory.Instance)