diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/UrlHelperExtensions.cs b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/UrlHelperExtensions.cs
index 05fc09c335..7767263b9d 100644
--- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/UrlHelperExtensions.cs
+++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Core/UrlHelperExtensions.cs
@@ -582,5 +582,57 @@ namespace Microsoft.AspNetCore.Mvc
return Action(helper, action, controller, values, protocol, host, fragment);
}
+
+ ///
+ /// Generates an absolute URL for a page, which contains the specified
+ /// , , route ,
+ /// to use, name, and .
+ /// Generates an absolute URL if the and are
+ /// non-null. See the remarks section for important security information.
+ ///
+ /// The .
+ /// The page name to generate the url for. When , defaults to the current executing page.
+ /// The handler to generate the url for. When , defaults to the current executing handler.
+ /// An object that contains route values.
+ /// The protocol for the URL, such as "http" or "https".
+ /// The host name for the URL.
+ /// The fragment for the URL.
+ /// The generated URL.
+ ///
+ ///
+ /// The value of should be a trusted value. Relying on the value of the current request
+ /// can allow untrusted input to influence the resulting URI unless the Host header has been validated.
+ /// See the deployment documentation for instructions on how to properly validate the Host header in
+ /// your deployment environment.
+ ///
+ ///
+ public static string PageLink(
+ this IUrlHelper urlHelper,
+ string pageName = null,
+ string pageHandler = null,
+ object values = null,
+ string protocol = null,
+ string host = null,
+ string fragment = null)
+ {
+ if (urlHelper == null)
+ {
+ throw new ArgumentNullException(nameof(urlHelper));
+ }
+
+ var httpContext = urlHelper.ActionContext.HttpContext;
+
+ if (protocol == null)
+ {
+ protocol = httpContext.Request.Protocol;
+ }
+
+ if (host == null)
+ {
+ host = httpContext.Request.Host.ToUriComponent();
+ }
+
+ return Page(urlHelper, pageName, pageHandler, values, protocol, host, fragment);
+ }
}
}
diff --git a/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperExtensionsTest.cs b/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperExtensionsTest.cs
index 8c9286e3f3..2f170b1c56 100644
--- a/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperExtensionsTest.cs
+++ b/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperExtensionsTest.cs
@@ -708,6 +708,151 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test.Routing
Assert.Equal(expectedHost, actual.Host);
}
+ [Fact]
+ public void PageLink_WithPageName_Works()
+ {
+ // Arrange
+ var expectedPage = "/TestPage";
+ var expectedProtocol = "testprotocol://";
+ var expectedHost = "www.example.com";
+ UrlRouteContext actual = null;
+
+ var actionContext = new ActionContext
+ {
+ HttpContext = new DefaultHttpContext
+ {
+ Request =
+ {
+ Protocol = expectedProtocol,
+ Host = new HostString(expectedHost),
+ }
+ },
+ RouteData = new RouteData
+ {
+ Values =
+ {
+ { "page", "ambient-page" },
+ }
+ },
+ };
+ var urlHelper = CreateMockUrlHelper(actionContext);
+ urlHelper.Setup(h => h.RouteUrl(It.IsAny()))
+ .Callback((UrlRouteContext context) => actual = context);
+
+ // Act
+ urlHelper.Object.PageLink(expectedPage);
+
+ // Assert
+ urlHelper.Verify();
+ Assert.NotNull(actual);
+ Assert.Collection(Assert.IsType(actual.Values),
+ value =>
+ {
+ Assert.Equal("page", value.Key);
+ Assert.Equal(expectedPage, value.Value);
+ });
+ Assert.Null(actual.RouteName);
+
+ Assert.Equal(expectedProtocol, actual.Protocol);
+ Assert.Equal(expectedHost, actual.Host);
+ }
+
+ [Fact]
+ public void PageLink_UsesSpecifiedProtocol()
+ {
+ // Arrange
+ var expectedProtocol = "testprotocol://";
+ var expectedHost = "www.example.com";
+ UrlRouteContext actual = null;
+
+ var actionContext = new ActionContext
+ {
+ HttpContext = new DefaultHttpContext
+ {
+ Request =
+ {
+ Protocol = "http://",
+ Host = new HostString(expectedHost),
+ }
+ },
+ RouteData = new RouteData
+ {
+ Values =
+ {
+ { "page", "ambient-page" },
+ }
+ },
+ };
+ var urlHelper = CreateMockUrlHelper(actionContext);
+ urlHelper.Setup(h => h.RouteUrl(It.IsAny()))
+ .Callback((UrlRouteContext context) => actual = context);
+
+ // Act
+ urlHelper.Object.PageLink(protocol: expectedProtocol);
+
+ // Assert
+ urlHelper.Verify();
+ Assert.NotNull(actual);
+ Assert.Collection(Assert.IsType(actual.Values),
+ value =>
+ {
+ Assert.Equal("page", value.Key);
+ Assert.Equal("ambient-page", value.Value);
+ });
+ Assert.Null(actual.RouteName);
+
+ Assert.Equal(expectedProtocol, actual.Protocol);
+ Assert.Equal(expectedHost, actual.Host);
+ }
+
+ [Fact]
+ public void PageLink_UsesSpecifiedHost()
+ {
+ // Arrange
+ var expectedProtocol = "testprotocol://";
+ var expectedHost = "www.example.com";
+ UrlRouteContext actual = null;
+
+ var actionContext = new ActionContext
+ {
+ HttpContext = new DefaultHttpContext
+ {
+ Request =
+ {
+ Protocol = expectedProtocol,
+ Host = new HostString("www.asp.net"),
+ }
+ },
+ RouteData = new RouteData
+ {
+ Values =
+ {
+ { "page", "ambient-page" },
+ }
+ },
+ };
+ var urlHelper = CreateMockUrlHelper(actionContext);
+ urlHelper.Setup(h => h.RouteUrl(It.IsAny()))
+ .Callback((UrlRouteContext context) => actual = context);
+
+ // Act
+ urlHelper.Object.PageLink(host: expectedHost);
+
+ // Assert
+ urlHelper.Verify();
+ Assert.NotNull(actual);
+ Assert.Collection(Assert.IsType(actual.Values),
+ value =>
+ {
+ Assert.Equal("page", value.Key);
+ Assert.Equal("ambient-page", value.Value);
+ });
+ Assert.Null(actual.RouteName);
+
+ Assert.Equal(expectedProtocol, actual.Protocol);
+ Assert.Equal(expectedHost, actual.Host);
+ }
+
private static Mock CreateMockUrlHelper(ActionContext context = null)
{
if (context == null)