From 927e7c8bfc449513a25a2f6e796c684c293c2cde Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 30 Aug 2018 15:14:34 +1200 Subject: [PATCH] Support route data tokens with Endpoint Routing (#8360) --- .../Internal/MvcEndpointDataSource.cs | 26 ++++++++++++------- .../Internal/MvcEndpointDataSourceTests.cs | 23 +--------------- .../RoutingTestsBase.cs | 24 +++++++++++++++++ .../Controllers/DataTokensController.cs | 15 +++++++++++ test/WebSites/RoutingWebSite/Startup.cs | 17 ++++++++---- .../RoutingWebSite/StartupWith21Compat.cs | 17 ++++++++---- 6 files changed, 80 insertions(+), 42 deletions(-) create mode 100644 test/WebSites/RoutingWebSite/Controllers/DataTokensController.cs diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs index 2d3f5e4ba2..c99e3a216d 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs @@ -174,6 +174,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal endpointInfo.Defaults, ++conventionalRouteOrder, endpointInfo, + endpointInfo.DataTokens, suppressLinkGeneration: false, suppressPathMatching: false); endpoints.Add(subEndpoint); @@ -214,6 +215,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal endpointInfo.Defaults, ++conventionalRouteOrder, endpointInfo, + endpointInfo.DataTokens, suppressLinkGeneration: false, suppressPathMatching: false); endpoints.Add(endpoint); @@ -229,6 +231,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal nonInlineDefaults: null, action.AttributeRouteInfo.Order, action.AttributeRouteInfo, + dataTokens: null, suppressLinkGeneration: action.AttributeRouteInfo.SuppressLinkGeneration, suppressPathMatching: action.AttributeRouteInfo.SuppressPathMatching); endpoints.Add(endpoint); @@ -378,20 +381,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal object nonInlineDefaults, int order, object source, + RouteValueDictionary dataTokens, bool suppressLinkGeneration, bool suppressPathMatching) { RequestDelegate requestDelegate = (context) => { - var values = context.Features.Get().RouteValues; - var routeData = new RouteData(); - foreach (var kvp in values) - { - if (kvp.Value != null) - { - routeData.Values.Add(kvp.Key, kvp.Value); - } - } + var routeData = context.GetRouteData(); var actionContext = new ActionContext(context, routeData, action); @@ -407,6 +403,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal routeName, new RouteValueDictionary(action.RouteValues), source, + dataTokens, suppressLinkGeneration, suppressPathMatching); @@ -425,6 +422,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal string routeName, RouteValueDictionary requiredValues, object source, + RouteValueDictionary dataTokens, bool suppressLinkGeneration, bool suppressPathMatching) { @@ -438,6 +436,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal metadata.AddRange(action.EndpointMetadata); } + if (dataTokens != null) + { + metadata.Add(new DataTokensMetadata(dataTokens)); + } + metadata.Add(new RouteValuesAddressMetadata(routeName, requiredValues)); // Add filter descriptors to endpoint metadata @@ -506,7 +509,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal { foreach (var kvp in requiredValues) { - defaults[kvp.Key] = kvp.Value; + if (kvp.Value != null) + { + defaults[kvp.Key] = kvp.Value; + } } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs index fe5d8dbbf7..a6f92ddc2c 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs @@ -89,6 +89,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var featureCollection = new FeatureCollection(); featureCollection.Set(endpointFeature); featureCollection.Set(endpointFeature); + featureCollection.Set(endpointFeature); var httpContextMock = new Mock(); httpContextMock.Setup(m => m.Features).Returns(featureCollection); @@ -694,28 +695,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults); } - [Fact] - public void RequiredValues_HavingNull_AndNotPresentInDefaultValues_IsAddedToDefaultValues() - { - // Arrange - var requiredValues = new RouteValueDictionary( - new { area = (string)null, controller = "Foo", action = "Bar", page = (string)null }); - var expectedDefaults = requiredValues; - var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues: requiredValues); - var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection); - dataSource.ConventionalEndpointInfos.Add( - CreateEndpointInfo(string.Empty, "{controller=Home}/{action=Index}")); - - // Act - var endpoints = dataSource.Endpoints; - - // Assert - var endpoint = Assert.Single(endpoints); - var matcherEndpoint = Assert.IsType(endpoint); - Assert.Equal("Foo/Bar", matcherEndpoint.RoutePattern.RawText); - AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults); - } - private MvcEndpointDataSource CreateMvcEndpointDataSource( IActionDescriptorCollectionProvider actionDescriptorCollectionProvider = null, MvcEndpointInvokerFactory mvcEndpointInvokerFactory = null) diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs index 6430ff2005..464a36beb5 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs @@ -72,6 +72,30 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests public string[] Routers { get; set; } } + [Fact] + public async Task DataTokens_ReturnsDataTokensForRoute() + { + // Arrange & Act + var response = await Client.GetAsync("http://localhost/DataTokensRoute/DataTokens/Index"); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + var result = JsonConvert.DeserializeObject>(body); + Assert.Single(result, kvp => kvp.Key == "hasDataTokens" && ((bool)kvp.Value) == true); + } + + [Fact] + public async Task DataTokens_ReturnsNoDataTokensForRoute() + { + // Arrange & Act + var response = await Client.GetAsync("http://localhost/DataTokens/Index"); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + var result = JsonConvert.DeserializeObject>(body); + Assert.Empty(result); + } + [Fact] public virtual async Task ConventionalRoutedController_ActionIsReachable() { diff --git a/test/WebSites/RoutingWebSite/Controllers/DataTokensController.cs b/test/WebSites/RoutingWebSite/Controllers/DataTokensController.cs new file mode 100644 index 0000000000..b6ae438777 --- /dev/null +++ b/test/WebSites/RoutingWebSite/Controllers/DataTokensController.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Mvc; + +namespace RoutingWebSite +{ + public class DataTokensController : Controller + { + public object Index() + { + return RouteData.DataTokens; + } + } +} \ No newline at end of file diff --git a/test/WebSites/RoutingWebSite/Startup.cs b/test/WebSites/RoutingWebSite/Startup.cs index 984e4cc5c2..cea64f826f 100644 --- a/test/WebSites/RoutingWebSite/Startup.cs +++ b/test/WebSites/RoutingWebSite/Startup.cs @@ -24,12 +24,19 @@ namespace RoutingWebSite { app.UseMvc(routes => { + routes.MapRoute( + "DataTokensRoute", + "DataTokensRoute/{controller}/{action}", + defaults: null, + constraints: new { controller = "DataTokens" }, + dataTokens: new { hasDataTokens = true }); + routes.MapAreaRoute( - "flightRoute", - "adminRoute", - "{area:exists}/{controller}/{action}", - new { controller = "Home", action = "Index" }, - new { area = "Travel" }); + "flightRoute", + "adminRoute", + "{area:exists}/{controller}/{action}", + defaults: new { controller = "Home", action = "Index" }, + constraints: new { area = "Travel" }); routes.MapRoute( "ActionAsMethod", diff --git a/test/WebSites/RoutingWebSite/StartupWith21Compat.cs b/test/WebSites/RoutingWebSite/StartupWith21Compat.cs index 33c10a0275..1b34b2cfb0 100644 --- a/test/WebSites/RoutingWebSite/StartupWith21Compat.cs +++ b/test/WebSites/RoutingWebSite/StartupWith21Compat.cs @@ -24,12 +24,19 @@ namespace RoutingWebSite { app.UseMvc(routes => { + routes.MapRoute( + "DataTokensRoute", + "DataTokensRoute/{controller}/{action}", + defaults: null, + constraints: new { controller = "DataTokens" }, + dataTokens: new { hasDataTokens = true }); + routes.MapAreaRoute( - "flightRoute", - "adminRoute", - "{area:exists}/{controller}/{action}", - new { controller = "Home", action = "Index" }, - new { area = "Travel" }); + "flightRoute", + "adminRoute", + "{area:exists}/{controller}/{action}", + defaults: new { controller = "Home", action = "Index" }, + constraints: new { area = "Travel" }); routes.MapRoute( "ActionAsMethod",