diff --git a/samples/RoutingSample.Web/UseEndpointRoutingStartup.cs b/samples/RoutingSample.Web/UseEndpointRoutingStartup.cs index 06dab7eddb..d67d5fbe2a 100644 --- a/samples/RoutingSample.Web/UseEndpointRoutingStartup.cs +++ b/samples/RoutingSample.Web/UseEndpointRoutingStartup.cs @@ -25,11 +25,11 @@ namespace RoutingSample.Web { options.ConstraintMap.Add("endsWith", typeof(EndsWithStringMatchProcessor)); }); - + services.Configure(options => { - options.DataSources.Add(new DefaultEndpointDataSource(new[] - { + options.DataSources.Add(new DefaultEndpointDataSource(new[] + { new MatcherEndpoint((next) => (httpContext) => { var response = httpContext.Response; @@ -83,7 +83,7 @@ namespace RoutingSample.Web EndpointMetadataCollection.Empty, "withoptionalconstraints"), })); - }); + }); } public void Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder app) diff --git a/src/Microsoft.AspNetCore.Routing.Abstractions/LinkGenerator.cs b/src/Microsoft.AspNetCore.Routing.Abstractions/LinkGenerator.cs index df5c1b5910..f4101a609a 100644 --- a/src/Microsoft.AspNetCore.Routing.Abstractions/LinkGenerator.cs +++ b/src/Microsoft.AspNetCore.Routing.Abstractions/LinkGenerator.cs @@ -1,13 +1,157 @@ // 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 Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing { public abstract class LinkGenerator { - public abstract bool TryGetLink(LinkGeneratorContext context, out string link); + public string GetLink(object values) + { + return GetLink(httpContext: null, routeName: null, values, options: null); + } - public abstract string GetLink(LinkGeneratorContext context); + public string GetLink(object values, LinkOptions options) + { + return GetLink(httpContext: null, routeName: null, values, options); + } + + public bool TryGetLink(object values, out string link) + { + return TryGetLink(httpContext: null, routeName: null, values, options: null, out link); + } + + public bool TryGetLink(object values, LinkOptions options, out string link) + { + return TryGetLink(httpContext: null, routeName: null, values, options, out link); + } + + public string GetLink(HttpContext httpContext, object values) + { + return GetLink(httpContext, routeName: null, values, options: null); + } + + public bool TryGetLink(HttpContext httpContext, object values, out string link) + { + return TryGetLink(httpContext, routeName: null, values, options: null, out link); + } + + public string GetLink(HttpContext httpContext, object values, LinkOptions options) + { + return GetLink(httpContext, routeName: null, values, options); + } + + public bool TryGetLink(HttpContext httpContext, object values, LinkOptions options, out string link) + { + return TryGetLink(httpContext, routeName: null, values, options, out link); + } + + public string GetLink(string routeName, object values) + { + return GetLink(httpContext: null, routeName, values, options: null); + } + + public bool TryGetLink(string routeName, object values, out string link) + { + return TryGetLink(httpContext: null, routeName, values, options: null, out link); + } + + public string GetLink(string routeName, object values, LinkOptions options) + { + return GetLink(httpContext: null, routeName, values, options); + } + + public bool TryGetLink(string routeName, object values, LinkOptions options, out string link) + { + return TryGetLink(httpContext: null, routeName, values, options, out link); + } + + public string GetLink(HttpContext httpContext, string routeName, object values) + { + return GetLink(httpContext, routeName, values, options: null); + } + + public bool TryGetLink(HttpContext httpContext, string routeName, object values, out string link) + { + return TryGetLink(httpContext, routeName, values, options: null, out link); + } + + public string GetLink(HttpContext httpContext, string routeName, object values, LinkOptions options) + { + if (TryGetLink(httpContext, routeName, values, options, out var link)) + { + return link; + } + + throw new InvalidOperationException("Could not find a matching endpoint to generate a link."); + } + + public abstract bool TryGetLink( + HttpContext httpContext, + string routeName, + object values, + LinkOptions options, + out string link); + + public string GetLinkByAddress(TAddress address, object values) + { + return GetLinkByAddress(address, httpContext: null, values, options: null); + } + + public bool TryGetLinkByAddress(TAddress address, object values, out string link) + { + return TryGetLinkByAddress(address, values, options: null, out link); + } + + public string GetLinkByAddress(TAddress address, object values, LinkOptions options) + { + return GetLinkByAddress(address, httpContext: null, values, options); + } + + public bool TryGetLinkByAddress( + TAddress address, + object values, + LinkOptions options, + out string link) + { + return TryGetLinkByAddress(address, httpContext: null, values, options, out link); + } + + public string GetLinkByAddress(TAddress address, HttpContext httpContext, object values) + { + return GetLinkByAddress(address, httpContext, values, options: null); + } + + public bool TryGetLinkByAddress( + TAddress address, + HttpContext httpContext, + object values, + out string link) + { + return TryGetLinkByAddress(address, httpContext, values, options: null, out link); + } + + public string GetLinkByAddress( + TAddress address, + HttpContext httpContext, + object values, + LinkOptions options) + { + if (TryGetLinkByAddress(address, httpContext, values, options, out var link)) + { + return link; + } + + throw new InvalidOperationException("Could not find a matching endpoint to generate a link."); + } + + public abstract bool TryGetLinkByAddress( + TAddress address, + HttpContext httpContext, + object values, + LinkOptions options, + out string link); } } diff --git a/src/Microsoft.AspNetCore.Routing.Abstractions/LinkGeneratorContext.cs b/src/Microsoft.AspNetCore.Routing.Abstractions/LinkOptions.cs similarity index 74% rename from src/Microsoft.AspNetCore.Routing.Abstractions/LinkGeneratorContext.cs rename to src/Microsoft.AspNetCore.Routing.Abstractions/LinkOptions.cs index c1e04c17e0..a5bd0547f0 100644 --- a/src/Microsoft.AspNetCore.Routing.Abstractions/LinkGeneratorContext.cs +++ b/src/Microsoft.AspNetCore.Routing.Abstractions/LinkOptions.cs @@ -1,21 +1,10 @@ // 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.Collections.Generic; -using Microsoft.AspNetCore.Http; - namespace Microsoft.AspNetCore.Routing { - public class LinkGeneratorContext + public class LinkOptions { - public HttpContext HttpContext { get; set; } - - public IEnumerable Endpoints { get; set; } - - public RouteValueDictionary ExplicitValues { get; set; } - - public RouteValueDictionary AmbientValues { get; set; } - /// /// Gets or sets a value indicating whether all generated paths URLs are lower-case. /// Use to configure the behavior for query strings. diff --git a/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs b/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs index b515643139..13d2869d32 100644 --- a/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs +++ b/src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.Template; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.Options; @@ -19,60 +20,106 @@ namespace Microsoft.AspNetCore.Routing private readonly static char[] UrlQueryDelimiters = new char[] { '?', '#' }; private readonly MatchProcessorFactory _matchProcessorFactory; private readonly ObjectPool _uriBuildingContextPool; - private readonly RouteOptions _options; private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; + private readonly RouteOptions _options; public DefaultLinkGenerator( MatchProcessorFactory matchProcessorFactory, ObjectPool uriBuildingContextPool, IOptions routeOptions, - ILogger logger) + ILogger logger, + IServiceProvider serviceProvider) { _matchProcessorFactory = matchProcessorFactory; _uriBuildingContextPool = uriBuildingContextPool; _options = routeOptions.Value; _logger = logger; + _serviceProvider = serviceProvider; } - public override string GetLink(LinkGeneratorContext context) + public override bool TryGetLink( + HttpContext httpContext, + string routeName, + object values, + LinkOptions options, + out string link) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - if (TryGetLink(context, out var link)) - { - return link; - } - - throw new InvalidOperationException("Could not find a matching endpoint to generate a link."); + return TryGetLinkByRouteValues( + httpContext, + routeName, + values, + options, + out link); } - public override bool TryGetLink(LinkGeneratorContext context, out string link) + public override bool TryGetLinkByAddress( + TAddress address, + HttpContext httpContext, + object values, + LinkOptions options, + out string link) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } + return TryGetLinkByAddressInternal( + address, + httpContext, + explicitValues: values, + ambientValues: GetAmbientValues(httpContext), + options, + out link); + } + private bool TryGetLinkByRouteValues( + HttpContext httpContext, + string routeName, + object values, + LinkOptions options, + out string link) + { + var ambientValues = GetAmbientValues(httpContext); + + var address = new RouteValuesAddress + { + RouteName = routeName, + ExplicitValues = new RouteValueDictionary(values), + AmbientValues = ambientValues + }; + + return TryGetLinkByAddressInternal( + address, + httpContext, + explicitValues: values, + ambientValues: ambientValues, + options, + out link); + } + + private bool TryGetLinkByAddressInternal( + TAddress address, + HttpContext httpContext, + object explicitValues, + RouteValueDictionary ambientValues, + LinkOptions options, + out string link) + { link = null; - if (context.Endpoints == null) + var endpointFinder = _serviceProvider.GetRequiredService>(); + var endpoints = endpointFinder.FindEndpoints(address); + if (endpoints == null) { return false; } - var matcherEndpoints = context.Endpoints.OfType(); + var matcherEndpoints = endpoints.OfType(); if (!matcherEndpoints.Any()) { - //todo:log here return false; } foreach (var endpoint in matcherEndpoints) { - link = GetLink(endpoint, context); + link = GetLink(endpoint); if (link != null) { return true; @@ -80,34 +127,33 @@ namespace Microsoft.AspNetCore.Routing } return false; - } - private string GetLink(MatcherEndpoint endpoint, LinkGeneratorContext context) - { - var templateBinder = new TemplateBinder( - UrlEncoder.Default, - _uriBuildingContextPool, - new RouteTemplate(endpoint.RoutePattern), - new RouteValueDictionary(endpoint.RoutePattern.Defaults)); - - var templateValuesResult = templateBinder.GetValues( - ambientValues: context.AmbientValues, - explicitValues: context.ExplicitValues, - endpoint.RequiredValues.Keys); - - if (templateValuesResult == null) + string GetLink(MatcherEndpoint endpoint) { - // We're missing one of the required values for this route. - return null; - } + var templateBinder = new TemplateBinder( + UrlEncoder.Default, + _uriBuildingContextPool, + new RouteTemplate(endpoint.RoutePattern), + new RouteValueDictionary(endpoint.RoutePattern.Defaults)); - if (!MatchesConstraints(context.HttpContext, endpoint, templateValuesResult.CombinedValues)) - { - return null; - } + var templateValuesResult = templateBinder.GetValues( + ambientValues: ambientValues, + explicitValues: new RouteValueDictionary(explicitValues), + requiredKeys: endpoint.RequiredValues.Keys); + if (templateValuesResult == null) + { + // We're missing one of the required values for this route. + return null; + } - var url = templateBinder.BindValues(templateValuesResult.AcceptedValues); - return Normalize(context, url); + if (!MatchesConstraints(httpContext, endpoint, templateValuesResult.CombinedValues)) + { + return null; + } + + var url = templateBinder.BindValues(templateValuesResult.AcceptedValues); + return Normalize(url, options); + } } private bool MatchesConstraints( @@ -138,13 +184,11 @@ namespace Microsoft.AspNetCore.Routing return true; } - private string Normalize(LinkGeneratorContext context, string url) + private string Normalize(string url, LinkOptions options) { - var lowercaseUrls = context.LowercaseUrls.HasValue ? context.LowercaseUrls.Value : _options.LowercaseUrls; - var lowercaseQueryStrings = context.LowercaseQueryStrings.HasValue ? - context.LowercaseQueryStrings.Value : _options.LowercaseQueryStrings; - var appendTrailingSlash = context.AppendTrailingSlash.HasValue ? - context.AppendTrailingSlash.Value : _options.AppendTrailingSlash; + var lowercaseUrls = options?.LowercaseUrls ?? _options.LowercaseUrls; + var lowercaseQueryStrings = options?.LowercaseQueryStrings ?? _options.LowercaseQueryStrings; + var appendTrailingSlash = options?.AppendTrailingSlash ?? _options.AppendTrailingSlash; if (!string.IsNullOrEmpty(url) && (lowercaseUrls || appendTrailingSlash)) { @@ -179,5 +223,18 @@ namespace Microsoft.AspNetCore.Routing return url; } + + private RouteValueDictionary GetAmbientValues(HttpContext httpContext) + { + if (httpContext != null) + { + var feature = httpContext.Features.Get(); + if (feature != null) + { + return feature.Values; + } + } + return null; + } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs index 6c5a47116e..1063db6819 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/DefaultLinkGeneratorTest.cs @@ -9,6 +9,8 @@ using Microsoft.AspNetCore.Routing.Constraints; using Microsoft.AspNetCore.Routing.Internal; using Microsoft.AspNetCore.Routing.Matching; using Microsoft.AspNetCore.Routing.TestObjects; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.Options; @@ -24,17 +26,10 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress(new { controller = "Home" }); + var linkGenerator = CreateLinkGenerator(endpoint); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(new { controller = "Home" }); // Assert Assert.Equal("/Home", link); @@ -46,18 +41,11 @@ namespace Microsoft.AspNetCore.Routing // Arrange var expectedMessage = "Could not find a matching endpoint to generate a link."; var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress(new { controller = "Home" }); + var linkGenerator = CreateLinkGenerator(endpoint); // Act & Assert var exception = Assert.Throws( - () => linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - })); + () => linkGenerator.GetLink(new { controller = "Home" })); Assert.Equal(expectedMessage, exception.Message); } @@ -66,17 +54,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress(new { controller = "Home" }); + var linkGenerator = CreateLinkGenerator(endpoint); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + new { controller = "Home" }, out var link); // Assert @@ -91,17 +73,10 @@ namespace Microsoft.AspNetCore.Routing var endpoint1 = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{id?}"); var endpoint2 = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint3 = EndpointFactory.CreateMatcherEndpoint("{controller}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress(new { controller = "Home", action = "Index", id = "10" }); + var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2, endpoint3); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint1, endpoint2, endpoint3 }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(new { controller = "Home", action = "Index", id = "10" }); // Assert Assert.Equal("/Home/Index/10", link); @@ -114,17 +89,10 @@ namespace Microsoft.AspNetCore.Routing var endpoint1 = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{id}"); var endpoint2 = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var endpoint3 = EndpointFactory.CreateMatcherEndpoint("{controller}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress(new { controller = "Home", action = "Index" }); + var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2, endpoint3); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint1, endpoint2, endpoint3 }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(new { controller = "Home", action = "Index" }); // Assert Assert.Equal("/Home/Index", link); @@ -135,19 +103,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { name = "name with %special #characters" }, - ambientValues: new { controller = "Home", action = "Index" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { name = "name with %special #characters" }); // Assert Assert.Equal("/Home/Index?name=name%20with%20%25special%20%23characters", link); @@ -158,19 +118,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - new { color = new List { "red", "green", "blue" } }, - new { controller = "Home", action = "Index" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var context = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(context, new { color = new List { "red", "green", "blue" } }); // Assert Assert.Equal("/Home/Index?color=red&color=green&color=blue", link); @@ -181,19 +133,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - new { items = new List { 10, 20, 30 } }, - new { controller = "Home", action = "Index" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { items = new List { 10, 20, 30 } }); // Assert Assert.Equal("/Home/Index?items=10&items=20&items=30", link); @@ -204,19 +148,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - new { color = new List { } }, - new { controller = "Home", action = "Index" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { color = new List { } }); // Assert Assert.Equal("/Home/Index", link); @@ -227,19 +163,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - new { page = 1, color = new List { "red", "green", "blue" }, message = "textfortest" }, - new { controller = "Home", action = "Index" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Index" }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { page = 1, color = new List { "red", "green", "blue" }, message = "textfortest" }); // Assert Assert.Equal("/Home/Index?page=1&color=red&color=green&color=blue&message=textfortest", link); @@ -250,19 +180,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index" }, - ambientValues: new { controller = "Home" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { action = "Index" }); // Assert Assert.Equal("/Home/Index", link); @@ -273,19 +195,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(new RouteOptions() { LowercaseUrls = true }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index" }, - ambientValues: new { controller = "Home" }); + var linkGenerator = CreateLinkGenerator(new[] { endpoint }, new RouteOptions() { LowercaseUrls = true }); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { action = "Index" }); // Assert Assert.Equal("/home/index", link); @@ -297,19 +211,14 @@ namespace Microsoft.AspNetCore.Routing // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var linkGenerator = CreateLinkGenerator( + new[] { endpoint }, new RouteOptions() { LowercaseUrls = true, LowercaseQueryStrings = true }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", ShowStatus = "True", INFO = "DETAILED" }, - ambientValues: new { controller = "Home" }); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", ShowStatus = "True", INFO = "DETAILED" }); // Assert Assert.Equal("/home/index?showstatus=true&info=detailed", link); @@ -321,19 +230,14 @@ namespace Microsoft.AspNetCore.Routing // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var linkGenerator = CreateLinkGenerator( + new[] { endpoint }, new RouteOptions() { LowercaseUrls = false, LowercaseQueryStrings = true }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", ShowStatus = "True", INFO = "DETAILED" }, - ambientValues: new { controller = "Home" }); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", ShowStatus = "True", INFO = "DETAILED" }); // Assert Assert.Equal("/Home/Index?ShowStatus=True&INFO=DETAILED", link); @@ -344,19 +248,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(new RouteOptions() { AppendTrailingSlash = true }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index" }, - ambientValues: new { controller = "Home" }); + var linkGenerator = CreateLinkGenerator( + new[] { endpoint }, + new RouteOptions() { AppendTrailingSlash = true }); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { action = "Index" }); // Assert Assert.Equal("/Home/Index/", link); @@ -368,19 +266,14 @@ namespace Microsoft.AspNetCore.Routing // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var linkGenerator = CreateLinkGenerator( + new[] { endpoint }, new RouteOptions() { LowercaseUrls = true, LowercaseQueryStrings = true, AppendTrailingSlash = true }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", ShowStatus = "True", INFO = "DETAILED" }, - ambientValues: new { controller = "Home" }); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", ShowStatus = "True", INFO = "DETAILED" }); // Assert Assert.Equal("/home/index/?showstatus=true&info=detailed", link); @@ -391,18 +284,17 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(new RouteOptions() { LowercaseUrls = true }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "InDex" }, - ambientValues: new { controller = "HoMe" }); + var linkGenerator = CreateLinkGenerator( + new[] { endpoint }, + new RouteOptions() { LowercaseUrls = true }); + var httpContext = CreateHttpContext(ambientValues: new { controller = "HoMe" }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext + httpContext, + values: new { action = "InDex" }, + new LinkOptions { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues, LowercaseUrls = false }); @@ -415,18 +307,17 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(new RouteOptions() { LowercaseUrls = false }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "InDex" }, - ambientValues: new { controller = "HoMe" }); + var linkGenerator = CreateLinkGenerator( + new[] { endpoint }, + new RouteOptions() { LowercaseUrls = false }); + var httpContext = CreateHttpContext(ambientValues: new { controller = "HoMe" }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext + httpContext, + values: new { action = "InDex" }, + new LinkOptions { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues, LowercaseUrls = true }); @@ -440,18 +331,16 @@ namespace Microsoft.AspNetCore.Routing // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var linkGenerator = CreateLinkGenerator( + new[] { endpoint }, new RouteOptions() { LowercaseUrls = true, LowercaseQueryStrings = true }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", ShowStatus = "True", INFO = "DETAILED" }, - ambientValues: new { controller = "Home" }); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext + httpContext, + values: new { action = "Index", ShowStatus = "True", INFO = "DETAILED" }, + new LinkOptions { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues, LowercaseUrls = false, LowercaseQueryStrings = false }); @@ -466,18 +355,16 @@ namespace Microsoft.AspNetCore.Routing // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); var linkGenerator = CreateLinkGenerator( + new[] { endpoint }, new RouteOptions() { LowercaseUrls = false, LowercaseQueryStrings = false }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", ShowStatus = "True", INFO = "DETAILED" }, - ambientValues: new { controller = "Home" }); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext + httpContext, + values: new { action = "Index", ShowStatus = "True", INFO = "DETAILED" }, + new LinkOptions { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues, LowercaseUrls = true, LowercaseQueryStrings = true }); @@ -491,18 +378,17 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}"); - var linkGenerator = CreateLinkGenerator(new RouteOptions() { AppendTrailingSlash = false }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index" }, - ambientValues: new { controller = "Home" }); + var linkGenerator = CreateLinkGenerator( + new[] { endpoint }, + new RouteOptions() { AppendTrailingSlash = false }); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home" }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext + httpContext, + values: new { action = "Index" }, + new LinkOptions { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues, AppendTrailingSlash = true }); @@ -514,23 +400,17 @@ namespace Microsoft.AspNetCore.Routing public void RouteGenerationRejectsConstraints() { // Arrange - var address = CreateRouteValuesAddress(new { p1 = "abcd" }); - var linkGenerator = CreateLinkGenerator(); - var endpoint = EndpointFactory.CreateMatcherEndpoint( "{p1}/{p2}", defaults: new { p2 = "catchall" }, constraints: new { p2 = "\\d{4}" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { p1 = "abcd" }, out var link); // Assert @@ -541,23 +421,17 @@ namespace Microsoft.AspNetCore.Routing public void RouteGenerationAcceptsConstraints() { // Arrange - var address = CreateRouteValuesAddress(new { p1 = "hello", p2 = "1234" }); - var linkGenerator = CreateLinkGenerator(); - var endpoint = EndpointFactory.CreateMatcherEndpoint( "{p1}/{p2}", defaults: new { p2 = "catchall" }, constraints: new { p2 = new RegexRouteConstraint("\\d{4}"), }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { p1 = "hello", p2 = "1234" }, out var link); // Assert @@ -569,23 +443,17 @@ namespace Microsoft.AspNetCore.Routing public void RouteWithCatchAllRejectsConstraints() { // Arrange - var address = CreateRouteValuesAddress(new { p1 = "abcd" }); - var linkGenerator = CreateLinkGenerator(); - var endpoint = EndpointFactory.CreateMatcherEndpoint( "{p1}/{*p2}", defaults: new { p2 = "catchall" }, constraints: new { p2 = new RegexRouteConstraint("\\d{4}") }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { p1 = "abcd" }, out var link); // Assert @@ -596,23 +464,17 @@ namespace Microsoft.AspNetCore.Routing public void RouteWithCatchAllAcceptsConstraints() { // Arrange - var address = CreateRouteValuesAddress(new { p1 = "hello", p2 = "1234" }); - var linkGenerator = CreateLinkGenerator(); - var endpoint = EndpointFactory.CreateMatcherEndpoint( "{p1}/{*p2}", defaults: new { p2 = "catchall" }, constraints: new { p2 = new RegexRouteConstraint("\\d{4}") }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { p1 = "hello", p2 = "1234" }, out var link); // Assert @@ -624,8 +486,6 @@ namespace Microsoft.AspNetCore.Routing public void GetLinkWithNonParameterConstraintReturnsUrlWithoutQueryString() { // Arrange - var address = CreateRouteValuesAddress(new { p1 = "hello", p2 = "1234" }); - var linkGenerator = CreateLinkGenerator(); var target = new Mock(); target .Setup( @@ -637,21 +497,17 @@ namespace Microsoft.AspNetCore.Routing It.IsAny())) .Returns(true) .Verifiable(); - var endpoint = EndpointFactory.CreateMatcherEndpoint( "{p1}/{p2}", defaults: new { p2 = "catchall" }, constraints: new { p2 = target.Object }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { p1 = "hello", p2 = "1234" }, out var link); // Assert @@ -667,28 +523,20 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var constraint = new CapturingConstraint(); - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "slug/Home/Store", defaults: new { controller = "Home", action = "Store" }, constraints: new { c = constraint }); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Store" }, + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext( ambientValues: new { controller = "Home", action = "Blog", extra = "42" }); - var expectedValues = new RouteValueDictionary( new { controller = "Home", action = "Store", extra = "42" }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { action = "Store" }, out var link); // Assert @@ -704,28 +552,17 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var constraint = new CapturingConstraint(); - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "slug/Home/Store", defaults: new { controller = "Home", action = "Store", otherthing = "17" }, constraints: new { c = constraint }); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Store" }, - ambientValues: new { controller = "Home", action = "Blog" }); - + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Blog" }); var expectedValues = new RouteValueDictionary( new { controller = "Home", action = "Store" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { action = "Store" }); // Assert Assert.Equal("/slug/Home/Store", link); @@ -738,28 +575,17 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var constraint = new CapturingConstraint(); - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "slug/{controller}/{action}", defaults: new { action = "Index" }, constraints: new { c = constraint, }); - - var address = CreateRouteValuesAddress( - explicitValues: new { controller = "Shopping" }, - ambientValues: new { controller = "Home", action = "Blog" }); - + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { controller = "Home", action = "Blog" }); var expectedValues = new RouteValueDictionary( new { controller = "Shopping", action = "Index" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { controller = "Shopping" }); // Assert Assert.Equal("/slug/Shopping", link); @@ -773,14 +599,12 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var constraint = new CapturingConstraint(); - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "slug/Home/Store", defaults: new { controller = "Home", action = "Store", otherthing = "17", thirdthing = "13" }, constraints: new { c = constraint, }); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Store", thirdthing = "13" }, + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext( ambientValues: new { controller = "Home", action = "Blog", otherthing = "17" }); var expectedValues = new RouteValueDictionary( @@ -788,13 +612,8 @@ namespace Microsoft.AspNetCore.Routing // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Store", thirdthing = "13" }); // Assert Assert.Equal("/slug/Home/Store", link); @@ -805,24 +624,17 @@ namespace Microsoft.AspNetCore.Routing public void GetLink_InlineConstraints_Success() { // Arrange - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "Home/Index/{id:int}", defaults: new { controller = "Home", action = "Index" }, constraints: new { }); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", id = 4 }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(new { }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", controller = "Home", id = 4 }); // Assert Assert.Equal("/Home/Index/4", link); @@ -832,24 +644,17 @@ namespace Microsoft.AspNetCore.Routing public void GetLink_InlineConstraints_NonMatchingvalue() { // Arrange - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "Home/Index/{id}", defaults: new { controller = "Home", action = "Index" }, constraints: new { id = "int" }); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", id = "not-an-integer" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { action = "Index", controller = "Home", id = "not-an-integer" }, out var link); // Assert @@ -860,23 +665,15 @@ namespace Microsoft.AspNetCore.Routing public void GetLink_InlineConstraints_OptionalParameter_ValuePresent() { // Arrange - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "Home/Index/{id:int?}", defaults: new { controller = "Home", action = "Index" }, constraints: new { }); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", id = 98 }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { action = "Index", controller = "Home", id = 98 }); // Assert Assert.Equal("/Home/Index/98", link); @@ -886,24 +683,15 @@ namespace Microsoft.AspNetCore.Routing public void GetLink_InlineConstraints_OptionalParameter_ValueNotPresent() { // Arrange - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "Home/Index/{id?}", defaults: new { controller = "Home", action = "Index" }, constraints: new { id = "int" }); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { action = "Index", controller = "Home" }); // Assert Assert.Equal("/Home/Index", link); @@ -913,24 +701,17 @@ namespace Microsoft.AspNetCore.Routing public void GetLink_InlineConstraints_OptionalParameter_ValuePresent_ConstraintFails() { // Arrange - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "Home/Index/{id?}", defaults: new { controller = "Home", action = "Index" }, constraints: new { id = "int" }); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", id = "not-an-integer" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { action = "Index", controller = "Home", id = "not-an-integer" }, out var link); // Assert @@ -941,24 +722,17 @@ namespace Microsoft.AspNetCore.Routing public void GetLink_InlineConstraints_MultipleInlineConstraints() { // Arrange - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "Home/Index/{id:int:range(1,20)}", defaults: new { controller = "Home", action = "Index" }, constraints: new { }); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", id = 14 }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", controller = "Home", id = 14 }); // Assert Assert.Equal("/Home/Index/14", link); @@ -968,24 +742,17 @@ namespace Microsoft.AspNetCore.Routing public void GetLink_InlineConstraints_CompositeInlineConstraint_Fails() { // Arrange - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "Home/Index/{id:int:range(1,20)}", defaults: new { controller = "Home", action = "Index" }, constraints: new { }); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", id = 50 }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { action = "Index", controller = "Home", id = 50 }, out var link); // Assert @@ -997,24 +764,17 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var constraint = new MaxLengthRouteConstraint(20); - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "Home/Index/{name}", defaults: new { controller = "Home", action = "Index" }, constraints: new { name = constraint }); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", name = "products" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", controller = "Home", name = "products" }); // Assert Assert.Equal("/Home/Index/products", link); @@ -1025,18 +785,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{name?}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", name = "products" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", controller = "Home", name = "products" }); // Assert Assert.Equal("/Home/Index/products", link); @@ -1047,18 +802,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{name?}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", controller = "Home" }); // Assert Assert.Equal("/Home/Index", link); @@ -1071,18 +821,13 @@ namespace Microsoft.AspNetCore.Routing var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "{controller}/{action}/{name}", defaults: new { name = "default-products" }); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", name = "products" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", controller = "Home", name = "products" }); // Assert Assert.Equal("/Home/Index/products", link); @@ -1095,18 +840,13 @@ namespace Microsoft.AspNetCore.Routing var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "{controller}/{action}/{name}", defaults: new { name = "products" }); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", controller = "Home" }); // Assert Assert.Equal("/Home/Index", link); @@ -1117,18 +857,13 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{name}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", name = "products", format = "json" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", controller = "Home", name = "products", format = "json" }); // Assert Assert.Equal("/Home/Index/products?format=json", link); @@ -1138,22 +873,15 @@ namespace Microsoft.AspNetCore.Routing public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterPresent() { // Arrange - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint( template: "{controller}/{action}/.{name?}"); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home", name = "products" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + httpContext, + new { action = "Index", controller = "Home", name = "products" }); // Assert Assert.Equal("/Home/Index/.products", link); @@ -1163,21 +891,12 @@ namespace Microsoft.AspNetCore.Routing public void GetLink_OptionalParameter_FollowedByDotAfterSlash_ParameterNotPresent() { // Arrange - var linkGenerator = CreateLinkGenerator(); var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/.{name?}"); - - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - HttpContext = new DefaultHttpContext(), - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { action = "Index", controller = "Home" }); // Assert Assert.Equal("/Home/Index/", link); @@ -1188,18 +907,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("{controller}/{action}/{name?}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { action = "Index", controller = "Home" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { action = "Index", controller = "Home" }); // Assert Assert.Equal("/Home/Index", link); @@ -1210,19 +922,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("a/{b=15}/{c?}/{d?}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { }, - ambientValues: new { c = "17" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { c = "17" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { }); // Assert Assert.Equal("/a/15/17", link); @@ -1233,19 +937,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("a/{b=15}/{c?}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { }, - ambientValues: new { c = "17" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { c = "17" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { }); // Assert Assert.Equal("/a/15/17", link); @@ -1256,19 +952,11 @@ namespace Microsoft.AspNetCore.Routing { // Arrange var endpoint = EndpointFactory.CreateMatcherEndpoint("a/{b=15}/{c?}/{d?}"); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { }, - ambientValues: new { d = "17" }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { d = "17" }); // Act - var link = linkGenerator.GetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }); + var link = linkGenerator.GetLink(httpContext, new { }); // Assert Assert.Equal("/a", link); @@ -1360,19 +1048,13 @@ namespace Microsoft.AspNetCore.Routing "Products/Edit/{id}", requiredValues: requiredValues, defaults: defaults); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: explicitValues, - ambientValues: ambientValues); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new RouteValueDictionary(explicitValues), out var link); // Assert @@ -1390,19 +1072,13 @@ namespace Microsoft.AspNetCore.Routing "Products/Edit/{id}", requiredValues: new { c = "Products", a = "Edit" }, defaults: new { c = "Products", a = "Edit" }); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { c = "Products", a = "Edit" }, - ambientValues: new { c = "Products", a = "Edit", id = 10 }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { c = "Products", a = "Edit", id = 10 }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { c = "Products", a = "Edit" }, out var link); // Assert @@ -1420,19 +1096,13 @@ namespace Microsoft.AspNetCore.Routing "Products/Edit/{id}", requiredValues: new { c = "Products", a = "Edit" }, defaults: new { c = "Products", a = "Edit" }); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: new { c = "Products", a = "List" }, - ambientValues: new { c = "Products", a = "Edit", id = 10 }); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues: new { c = "Products", a = "Edit", id = 10 }); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new { c = "Products", a = "List" }, out var link); // Assert @@ -1532,19 +1202,13 @@ namespace Microsoft.AspNetCore.Routing "Products/Edit/{id}", requiredValues: requiredValues, defaults: defaults); - var linkGenerator = CreateLinkGenerator(); - var address = CreateRouteValuesAddress( - explicitValues: explicitValues, - ambientValues: ambientValues); + var linkGenerator = CreateLinkGenerator(endpoint); + var httpContext = CreateHttpContext(ambientValues); // Act var canGenerateLink = linkGenerator.TryGetLink( - new LinkGeneratorContext - { - Endpoints = new[] { endpoint }, - ExplicitValues = address.ExplicitValues, - AmbientValues = address.AmbientValues - }, + httpContext, + new RouteValueDictionary(explicitValues), out var link); // Assert @@ -1552,27 +1216,154 @@ namespace Microsoft.AspNetCore.Routing Assert.Null(link); } - private RouteValuesAddress CreateRouteValuesAddress( - object explicitValues, - object ambientValues = null) + [Fact] + public void TryGetLink_WithCustomAddress_CanGenerateLink() { - var address = new RouteValuesAddress(); - address.ExplicitValues = new RouteValueDictionary(explicitValues); - address.AmbientValues = new RouteValueDictionary(ambientValues); - return address; + // Arrange + var services = GetBasicServices(); + services.TryAddEnumerable( + ServiceDescriptor.Singleton, EndpointFinderByName>()); + var endpoint1 = EndpointFactory.CreateMatcherEndpoint( + "Products/Details/{id}", + requiredValues: new { controller = "Products", action = "Details" }, + defaults: new { controller = "Products", action = "Details" }); + var endpoint2 = EndpointFactory.CreateMatcherEndpoint( + "Customers/Details/{id}", + requiredValues: new { controller = "Customers", action = "Details" }, + defaults: new { controller = "Customers", action = "Details" }, + metadata: new NameMetadata("CustomerDetails")); + var linkGenerator = CreateLinkGenerator(new[] { endpoint1, endpoint2 }, new RouteOptions(), services); + var httpContext = CreateHttpContext(ambientValues: new { }); + + // Act + var canGenerateLink = linkGenerator.TryGetLinkByAddress( + address: new NameMetadata("CustomerDetails"), + httpContext, + values: new { id = 10 }, + out var link); + + // Assert + Assert.True(canGenerateLink); + Assert.Equal("/Customers/Details/10", link); } - private LinkGenerator CreateLinkGenerator(RouteOptions routeOptions = null) + [Fact] + public void TryGetLink_WithCustomAddress_CanGenerateLink_RespectsLinkOptions_SuppliedAtCallSite() { + // Arrange + var services = GetBasicServices(); + services.TryAddEnumerable( + ServiceDescriptor.Singleton, EndpointFinderByName>()); + var endpoint1 = EndpointFactory.CreateMatcherEndpoint( + "Products/Details/{id}", + requiredValues: new { controller = "Products", action = "Details" }, + defaults: new { controller = "Products", action = "Details" }); + var endpoint2 = EndpointFactory.CreateMatcherEndpoint( + "Customers/Details/{id}", + requiredValues: new { controller = "Customers", action = "Details" }, + defaults: new { controller = "Customers", action = "Details" }, + metadata: new NameMetadata("CustomerDetails")); + var linkGenerator = CreateLinkGenerator(new[] { endpoint1, endpoint2 }, new RouteOptions(), services); + var httpContext = CreateHttpContext(ambientValues: new { }); + + // Act + var canGenerateLink = linkGenerator.TryGetLinkByAddress( + address: new NameMetadata("CustomerDetails"), + httpContext, + values: new { id = 10 }, + new LinkOptions + { + LowercaseUrls = true + }, + out var link); + + // Assert + Assert.True(canGenerateLink); + Assert.Equal("/customers/details/10", link); + } + + private LinkGenerator CreateLinkGenerator(params Endpoint[] endpoints) + { + return CreateLinkGenerator(endpoints, routeOptions: null); + } + + private LinkGenerator CreateLinkGenerator( + Endpoint[] endpoints, + RouteOptions routeOptions, + ServiceCollection services = null) + { + if (services == null) + { + services = GetBasicServices(); + } + + if (endpoints != null || endpoints.Length > 0) + { + services.Configure(o => + { + o.DataSources.Add(new DefaultEndpointDataSource(endpoints)); + }); + } + routeOptions = routeOptions ?? new RouteOptions(); var options = Options.Create(routeOptions); + var serviceProvider = services.BuildServiceProvider(); + return new DefaultLinkGenerator( - new DefaultMatchProcessorFactory( - options, - Mock.Of()), + new DefaultMatchProcessorFactory(options, serviceProvider), new DefaultObjectPool(new UriBuilderContextPooledObjectPolicy()), options, - NullLogger.Instance); + NullLogger.Instance, + serviceProvider); + } + + private HttpContext CreateHttpContext(object ambientValues) + { + var httpContext = new DefaultHttpContext(); + httpContext.Features.Set(new EndpointFeature + { + Values = new RouteValueDictionary(ambientValues) + }); + return httpContext; + } + + private ServiceCollection GetBasicServices() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddOptions(); + services.AddRouting(); + services.AddLogging(); + return services; + } + + private class EndpointFinderByName : IEndpointFinder + { + private readonly CompositeEndpointDataSource _dataSource; + + public EndpointFinderByName(CompositeEndpointDataSource dataSource) + { + _dataSource = dataSource; + } + + public IEnumerable FindEndpoints(INameMetadata address) + { + var endpoint = _dataSource.Endpoints.SingleOrDefault(e => + { + var nameMetadata = e.Metadata.GetMetadata(); + return nameMetadata != null && string.Equals(address.Name, nameMetadata.Name); + }); + return new[] { endpoint }; + } + } + + private class NameMetadata : INameMetadata + { + public NameMetadata(string name) + { + Name = name; + } + public string Name { get; } } } }