diff --git a/build/dependencies.props b/build/dependencies.props index 6697d0589f..3015d4e3c2 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -39,7 +39,7 @@ 2.2.0-preview1-34869 2.2.0-preview1-34869 2.2.0-preview1-34869 - 2.2.0-preview1-34869 + 2.2.0-a-preview1-routing-lg-16736 2.2.0-preview1-34869 2.2.0-preview1-34869 2.2.0-preview1-34869 @@ -48,8 +48,8 @@ 2.2.0-preview1-34869 2.2.0-preview1-34869 2.2.0-preview1-34869 - 2.2.0-preview1-34869 - 2.2.0-preview1-34869 + 2.2.0-a-preview1-change-linkgenerator-api-16845 + 2.2.0-a-preview1-change-linkgenerator-api-16845 2.2.0-preview1-34869 2.2.0-preview1-34869 2.2.0-preview1-34869 diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs index f24859eb20..27a548c37a 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs @@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Routing; -using Microsoft.AspNetCore.Routing.Metadata; using Resources = Microsoft.AspNetCore.Mvc.Core.Resources; namespace Microsoft.AspNetCore.Mvc.Internal diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs index 00ad6bf594..c79b2b209d 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Routing; -using Microsoft.AspNetCore.Routing.Metadata; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Internal; using Microsoft.Extensions.Options; diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs index 3770856e64..9e4ff9b92c 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs @@ -3,17 +3,14 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; -using Microsoft.AspNetCore.Mvc.ActionConstraints; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Matching; -using Microsoft.AspNetCore.Routing.Metadata; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Template; using Microsoft.Extensions.Primitives; @@ -295,11 +292,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal var defaults = new RouteValueDictionary(nonInlineDefaults); EnsureRequiredValuesInDefaults(action.RouteValues, defaults); - var metadataCollection = BuildEndpointMetadata(action, routeName, source, suppressLinkGeneration); + var metadataCollection = BuildEndpointMetadata( + action, + routeName, + new RouteValueDictionary(action.RouteValues), + source, + suppressLinkGeneration); + var endpoint = new MatcherEndpoint( next => invokerDelegate, RoutePatternFactory.Parse(template, defaults, constraints: null), - new RouteValueDictionary(action.RouteValues), order, metadataCollection, action.DisplayName); @@ -310,6 +312,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal private static EndpointMetadataCollection BuildEndpointMetadata( ActionDescriptor action, string routeName, + RouteValueDictionary requiredValues, object source, bool suppressLinkGeneration) { @@ -323,10 +326,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal metadata.AddRange(action.EndpointMetadata); } - if (!string.IsNullOrEmpty(routeName)) - { - metadata.Add(new RouteNameMetadata(routeName)); - } + metadata.Add(new RouteValuesAddressMetadata(routeName, requiredValues)); // Add filter descriptors to endpoint metadata if (action.FilterDescriptors != null && action.FilterDescriptors.Count > 0) @@ -349,7 +349,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal { metadata.Add(new HttpMethodMetadata(httpMethodActionConstraint.HttpMethods)); } - else if (actionConstraint is ConsumesAttribute consumesAttribute && + else if (actionConstraint is ConsumesAttribute consumesAttribute && !metadata.OfType().Any()) { metadata.Add(new ConsumesMetadata(consumesAttribute.ContentTypes.ToArray())); @@ -444,16 +444,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal public List ConventionalEndpointInfos { get; } - private class RouteNameMetadata : IRouteNameMetadata - { - public RouteNameMetadata(string routeName) - { - Name = routeName; - } - - public string Name { get; } - } - private class SuppressLinkGenerationMetadata : ISuppressLinkGenerationMetadata { } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Routing/ConsumesMatcherPolicy.cs b/src/Microsoft.AspNetCore.Mvc.Core/Routing/ConsumesMatcherPolicy.cs index dcaeaca936..3adacf3c38 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Routing/ConsumesMatcherPolicy.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Routing/ConsumesMatcherPolicy.cs @@ -142,7 +142,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing return Task.CompletedTask; }, RoutePatternFactory.Parse("/"), - new RouteValueDictionary(), 0, EndpointMetadataCollection.Empty, Http415EndpointDisplayName); diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Routing/EndpointRoutingUrlHelper.cs b/src/Microsoft.AspNetCore.Mvc.Core/Routing/EndpointRoutingUrlHelper.cs index 6f24248950..8ddd49c2f1 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Routing/EndpointRoutingUrlHelper.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Routing/EndpointRoutingUrlHelper.cs @@ -15,21 +15,16 @@ namespace Microsoft.AspNetCore.Mvc.Routing { private readonly ILogger _logger; private readonly LinkGenerator _linkGenerator; - private readonly IEndpointFinder _routeValuesBasedEndpointFinder; /// /// Initializes a new instance of the class using the specified /// . /// /// The for the current request. - /// - /// The which finds endpoints by required route values. - /// /// The used to generate the link. /// The . public EndpointRoutingUrlHelper( ActionContext actionContext, - IEndpointFinder routeValuesBasedEndpointFinder, LinkGenerator linkGenerator, ILogger logger) : base(actionContext) @@ -45,7 +40,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing } _linkGenerator = linkGenerator; - _routeValuesBasedEndpointFinder = routeValuesBasedEndpointFinder; _logger = logger; } @@ -85,23 +79,10 @@ namespace Microsoft.AspNetCore.Mvc.Routing valuesDictionary["controller"] = urlActionContext.Controller; } - var endpoints = _routeValuesBasedEndpointFinder.FindEndpoints( - new RouteValuesAddress() - { - ExplicitValues = valuesDictionary, - AmbientValues = AmbientValues - }); - var successfullyGeneratedLink = _linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = ActionContext.HttpContext, - Endpoints = endpoints, - ExplicitValues = valuesDictionary, - AmbientValues = AmbientValues - }, + ActionContext.HttpContext, + valuesDictionary, out var link); - if (!successfullyGeneratedLink) { //TODO: log here @@ -122,22 +103,10 @@ namespace Microsoft.AspNetCore.Mvc.Routing var valuesDictionary = routeContext.Values as RouteValueDictionary ?? GetValuesDictionary(routeContext.Values); - var endpoints = _routeValuesBasedEndpointFinder.FindEndpoints( - new RouteValuesAddress() - { - RouteName = routeContext.RouteName, - ExplicitValues = valuesDictionary, - AmbientValues = AmbientValues - }); - var successfullyGeneratedLink = _linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = ActionContext.HttpContext, - Endpoints = endpoints, - ExplicitValues = valuesDictionary, - AmbientValues = AmbientValues - }, + ActionContext.HttpContext, + routeContext.RouteName, + valuesDictionary, out var link); if (!successfullyGeneratedLink) diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Routing/UrlHelperFactory.cs b/src/Microsoft.AspNetCore.Mvc.Core/Routing/UrlHelperFactory.cs index fb6bbb0ae5..933f339f30 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Routing/UrlHelperFactory.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Routing/UrlHelperFactory.cs @@ -51,12 +51,10 @@ namespace Microsoft.AspNetCore.Mvc.Routing { var services = httpContext.RequestServices; var linkGenerator = services.GetRequiredService(); - var routeValuesBasedEndpointFinder = services.GetRequiredService>(); var logger = services.GetRequiredService>(); urlHelper = new EndpointRoutingUrlHelper( context, - routeValuesBasedEndpointFinder, linkGenerator, logger); } diff --git a/src/Microsoft.AspNetCore.Mvc.Cors/Internal/CorsApplicationModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.Cors/Internal/CorsApplicationModelProvider.cs index 3c5d23010d..352a8b4ee6 100644 --- a/src/Microsoft.AspNetCore.Mvc.Cors/Internal/CorsApplicationModelProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Cors/Internal/CorsApplicationModelProvider.cs @@ -6,7 +6,7 @@ using System.Linq; using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Internal; -using Microsoft.AspNetCore.Routing.Metadata; +using Microsoft.AspNetCore.Routing; namespace Microsoft.AspNetCore.Mvc.Cors.Internal { diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs index db7a1e3ce1..060543dd8b 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs @@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Routing; -using Microsoft.AspNetCore.Routing.Metadata; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Options; using Moq; using Xunit; diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs index 6304099fc0..bc8d0dbcdc 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs @@ -66,7 +66,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal var endpoint = Assert.Single(endpoints); var matcherEndpoint = Assert.IsType(endpoint); - var endpointValue = matcherEndpoint.RequiredValues["Name"]; + var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata(); + Assert.NotNull(routeValuesAddressMetadata); + var endpointValue = routeValuesAddressMetadata.RequiredValues["Name"]; Assert.Equal(routeValue, endpointValue); Assert.Equal(displayName, matcherEndpoint.DisplayName); @@ -389,7 +391,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal } [Fact] - public void Endpoints_ConventionalRoute_WithNoRouteName_DoesNotAddRouteNameMetadata() + public void Endpoints_ConventionalRoute_WithEmptyRouteName_CreatesMetadataWithEmptyRouteName() { // Arrange var actionDescriptorCollection = GetActionDescriptorCollection( @@ -404,8 +406,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal // Assert var endpoint = Assert.Single(endpoints); var matcherEndpoint = Assert.IsType(endpoint); - var routeNameMetadata = matcherEndpoint.Metadata.GetMetadata(); - Assert.Null(routeNameMetadata); + var routeValuesAddressNameMetadata = matcherEndpoint.Metadata.GetMetadata(); + Assert.NotNull(routeValuesAddressNameMetadata); + Assert.Equal(string.Empty, routeValuesAddressNameMetadata.Name); } [Fact] @@ -428,17 +431,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal (ep) => { var matcherEndpoint = Assert.IsType(ep); - var routeNameMetadata = matcherEndpoint.Metadata.GetMetadata(); - Assert.NotNull(routeNameMetadata); - Assert.Equal("namedRoute", routeNameMetadata.Name); + var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata(); + Assert.NotNull(routeValuesAddressMetadata); + Assert.Equal("namedRoute", routeValuesAddressMetadata.Name); Assert.Equal("named/Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText); }, (ep) => { var matcherEndpoint = Assert.IsType(ep); - var routeNameMetadata = matcherEndpoint.Metadata.GetMetadata(); - Assert.NotNull(routeNameMetadata); - Assert.Equal("namedRoute", routeNameMetadata.Name); + var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata(); + Assert.NotNull(routeValuesAddressMetadata); + Assert.Equal("namedRoute", routeValuesAddressMetadata.Name); Assert.Equal("named/Products/Details/{id?}", matcherEndpoint.RoutePattern.RawText); }); } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/ActionConstraintMatcherPolicyTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/ActionConstraintMatcherPolicyTest.cs index 77288f75d3..31e3dd8653 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/ActionConstraintMatcherPolicyTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/ActionConstraintMatcherPolicyTest.cs @@ -1,15 +1,11 @@ // 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 System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ActionConstraints; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Routing; @@ -379,7 +375,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing return new MatcherEndpoint( (r) => null, RoutePatternFactory.Parse("/"), - new RouteValueDictionary(), 0, new EndpointMetadataCollection(metadata), $"test: {action?.DisplayName}"); diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/ConsumesMatcherPolicyTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/ConsumesMatcherPolicyTest.cs index d52ddebf74..48a49e465e 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/ConsumesMatcherPolicyTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/ConsumesMatcherPolicyTest.cs @@ -223,7 +223,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing return new MatcherEndpoint( (next) => null, RoutePatternFactory.Parse(template), - new RouteValueDictionary(), 0, new EndpointMetadataCollection(metadata), $"test: {template} - {string.Join(", ", consumesMetadata?.ContentTypes ?? Array.Empty())}"); diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/EndpointRoutingUrlHelperTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/EndpointRoutingUrlHelperTest.cs index 867832e808..e02dd3c5c9 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/EndpointRoutingUrlHelperTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/EndpointRoutingUrlHelperTest.cs @@ -56,7 +56,17 @@ namespace Microsoft.AspNetCore.Mvc.Routing requiredValues: new { controller = "Orders", action = "GetAll" }, routeName: "OrdersApi"); var urlHelper = CreateUrlHelper(new[] { endpoint1, endpoint2 }); - urlHelper.ActionContext.RouteData.Values["id"] = "500"; + + // Set the endpoint feature and current context just as a normal request to MVC app would be + var endpointFeature = new EndpointFeature(); + urlHelper.ActionContext.HttpContext.Features.Set(endpointFeature); + endpointFeature.Endpoint = endpoint1; + endpointFeature.Values = new RouteValueDictionary + { + ["controller"] = "Orders", + ["action"] = "GetById", + ["id"] = "500" + }; // Act var url = urlHelper.RouteUrl( @@ -132,7 +142,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing endpoints.Add(new MatcherEndpoint( next => httpContext => Task.CompletedTask, RoutePatternFactory.Parse(template), - new RouteValueDictionary(), 0, EndpointMetadataCollection.Empty, null)); @@ -147,7 +156,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing Endpoint = new MatcherEndpoint( next => cntxt => Task.CompletedTask, RoutePatternFactory.Parse("/"), - new RouteValueDictionary(), 0, EndpointMetadataCollection.Empty, null) @@ -280,17 +288,13 @@ namespace Microsoft.AspNetCore.Mvc.Routing { if (metadataCollection == null) { - metadataCollection = EndpointMetadataCollection.Empty; - if (!string.IsNullOrEmpty(routeName)) - { - metadataCollection = new EndpointMetadataCollection(new[] { new RouteNameMetadata(routeName) }); - } + metadataCollection = new EndpointMetadataCollection( + new RouteValuesAddressMetadata(routeName, new RouteValueDictionary(requiredValues))); } return new MatcherEndpoint( next => (httpContext) => Task.CompletedTask, RoutePatternFactory.Parse(template, defaults, constraints: null), - new RouteValueDictionary(requiredValues), order, metadataCollection, null); @@ -316,22 +320,11 @@ namespace Microsoft.AspNetCore.Mvc.Routing return new MatcherEndpoint( next => c => Task.CompletedTask, RoutePatternFactory.Parse(template, defaults, constraints: null), - new RouteValueDictionary(), 0, EndpointMetadataCollection.Empty, null); } - private class RouteNameMetadata : IRouteNameMetadata - { - public RouteNameMetadata(string routeName) - { - Name = routeName; - } - - public string Name { get; } - } - private class SuppressLinkGenerationMetadata : ISuppressLinkGenerationMetadata { } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Cors.Test/Internal/CorsApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Cors.Test/Internal/CorsApplicationModelProviderTest.cs index a9538a9b24..4543e2e8cd 100644 --- a/test/Microsoft.AspNetCore.Mvc.Cors.Test/Internal/CorsApplicationModelProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Cors.Test/Internal/CorsApplicationModelProviderTest.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Routing.Metadata; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq;