diff --git a/build/dependencies.props b/build/dependencies.props
index 712c6521e9..4de4f3a0fd 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -48,8 +48,8 @@
2.2.0-preview2-35143
2.2.0-preview2-35143
2.2.0-preview2-35143
- 2.2.0-preview2-35143
- 2.2.0-preview2-35143
+ 2.2.0-a-preview3-link-generator-16951
+ 2.2.0-a-preview3-link-generator-16951
2.2.0-preview2-35143
2.2.0-preview2-35143
2.2.0-preview2-35143
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Routing/EndpointRoutingUrlHelper.cs b/src/Microsoft.AspNetCore.Mvc.Core/Routing/EndpointRoutingUrlHelper.cs
index 8ddd49c2f1..adc94a7d8e 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Routing/EndpointRoutingUrlHelper.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Routing/EndpointRoutingUrlHelper.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging;
@@ -51,46 +52,41 @@ namespace Microsoft.AspNetCore.Mvc.Routing
throw new ArgumentNullException(nameof(urlActionContext));
}
- var valuesDictionary = GetValuesDictionary(urlActionContext.Values);
+ var values = GetValuesDictionary(urlActionContext.Values);
if (urlActionContext.Action == null)
{
- if (!valuesDictionary.ContainsKey("action") &&
+ if (!values.ContainsKey("action") &&
AmbientValues.TryGetValue("action", out var action))
{
- valuesDictionary["action"] = action;
+ values["action"] = action;
}
}
else
{
- valuesDictionary["action"] = urlActionContext.Action;
+ values["action"] = urlActionContext.Action;
}
if (urlActionContext.Controller == null)
{
- if (!valuesDictionary.ContainsKey("controller") &&
+ if (!values.ContainsKey("controller") &&
AmbientValues.TryGetValue("controller", out var controller))
{
- valuesDictionary["controller"] = controller;
+ values["controller"] = controller;
}
}
else
{
- valuesDictionary["controller"] = urlActionContext.Controller;
+ values["controller"] = urlActionContext.Controller;
}
- var successfullyGeneratedLink = _linkGenerator.TryGetLink(
+
+ var path = _linkGenerator.GetPathByRouteValues(
ActionContext.HttpContext,
- valuesDictionary,
- out var link);
- if (!successfullyGeneratedLink)
- {
- //TODO: log here
-
- return null;
- }
-
- return GenerateUrl(urlActionContext.Protocol, urlActionContext.Host, link, urlActionContext.Fragment);
+ routeName: null,
+ values,
+ new FragmentString(urlActionContext.Fragment == null ? null : "#" + urlActionContext.Fragment));
+ return GenerateUrl(urlActionContext.Protocol, urlActionContext.Host, path);
}
///
@@ -101,20 +97,12 @@ namespace Microsoft.AspNetCore.Mvc.Routing
throw new ArgumentNullException(nameof(routeContext));
}
- var valuesDictionary = routeContext.Values as RouteValueDictionary ?? GetValuesDictionary(routeContext.Values);
-
- var successfullyGeneratedLink = _linkGenerator.TryGetLink(
+ var path = _linkGenerator.GetPathByRouteValues(
ActionContext.HttpContext,
routeContext.RouteName,
- valuesDictionary,
- out var link);
-
- if (!successfullyGeneratedLink)
- {
- return null;
- }
-
- return GenerateUrl(routeContext.Protocol, routeContext.Host, link, routeContext.Fragment);
+ routeContext.Values,
+ new FragmentString(routeContext.Fragment == null ? null : "#" + routeContext.Fragment));
+ return GenerateUrl(routeContext.Protocol, routeContext.Host, path);
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Routing/UrlHelperBase.cs b/src/Microsoft.AspNetCore.Mvc.Core/Routing/UrlHelperBase.cs
index 730323f33b..a68c9c988b 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Routing/UrlHelperBase.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Routing/UrlHelperBase.cs
@@ -201,6 +201,68 @@ namespace Microsoft.AspNetCore.Mvc.Routing
}
}
+ ///
+ /// Generates a URI from the provided components.
+ ///
+ /// The URI scheme/protocol.
+ /// The URI host.
+ /// The URI path and remaining portions (path, query, and fragment).
+ ///
+ /// An absolute URI if the or is specified, otherwise generates a
+ /// URI with an absolute path.
+ ///
+ protected string GenerateUrl(string protocol, string host, string path)
+ {
+ // This method is similar to GenerateUrl, but it's used for EndpointRouting. It ignores pathbase and fragment
+ // because those have already been incorporated.
+ if (path == null)
+ {
+ return null;
+ }
+
+ // Perf: In most of the common cases, GenerateUrl is called with a null protocol, host and fragment.
+ // In such cases, we might not need to build any URL as the url generated is mostly same as the virtual path available in pathData.
+ // For such common cases, this FastGenerateUrl method saves a string allocation per GenerateUrl call.
+ string url;
+ if (TryFastGenerateUrl(protocol, host, path, fragment: null, out url))
+ {
+ return url;
+ }
+
+ var builder = GetStringBuilder();
+ try
+ {
+ if (string.IsNullOrEmpty(protocol) && string.IsNullOrEmpty(host))
+ {
+ AppendPathAndFragment(builder, pathBase: null, path, fragment: null);
+
+ // We're returning a partial URL (just path + query + fragment), but we still want it to be rooted.
+ if (builder.Length == 0 || builder[0] != '/')
+ {
+ builder.Insert(0, '/');
+ }
+ }
+ else
+ {
+ protocol = string.IsNullOrEmpty(protocol) ? "http" : protocol;
+ builder.Append(protocol);
+
+ builder.Append("://");
+
+ host = string.IsNullOrEmpty(host) ? ActionContext.HttpContext.Request.Host.Value : host;
+ builder.Append(host);
+ AppendPathAndFragment(builder, pathBase: null, path, fragment: null);
+ }
+
+ return builder.ToString();
+ }
+ finally
+ {
+ // Clear the StringBuilder so that it can reused for the next call.
+ builder.Clear();
+ }
+ }
+
// for unit testing
internal static void AppendPathAndFragment(StringBuilder builder, PathString pathBase, string virtualPath, string fragment)
{
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs
index a6f92ddc2c..e15b8225e0 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/MvcEndpointDataSourceTests.cs
@@ -377,7 +377,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var matcherEndpoint = Assert.IsType(endpoint);
var routeValuesAddressNameMetadata = matcherEndpoint.Metadata.GetMetadata();
Assert.NotNull(routeValuesAddressNameMetadata);
- Assert.Equal(string.Empty, routeValuesAddressNameMetadata.Name);
+ Assert.Equal(string.Empty, routeValuesAddressNameMetadata.RouteName);
}
[Fact]
@@ -402,7 +402,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var matcherEndpoint = Assert.IsType(ep);
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata();
Assert.NotNull(routeValuesAddressMetadata);
- Assert.Equal("namedRoute", routeValuesAddressMetadata.Name);
+ Assert.Equal("namedRoute", routeValuesAddressMetadata.RouteName);
Assert.Equal("named/Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
},
(ep) =>
@@ -410,7 +410,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var matcherEndpoint = Assert.IsType(ep);
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata();
Assert.NotNull(routeValuesAddressMetadata);
- Assert.Equal("namedRoute", routeValuesAddressMetadata.Name);
+ Assert.Equal("namedRoute", routeValuesAddressMetadata.RouteName);
Assert.Equal("named/Products/Details/{id?}", matcherEndpoint.RoutePattern.RawText);
});
}