From 1bc056a7f6048491ceaf5a0e9d16274314bd13ef Mon Sep 17 00:00:00 2001 From: Patrick Westerhoff Date: Tue, 29 Jan 2019 00:42:10 +0100 Subject: [PATCH] Introduce IUrlHelper.PageLink (#6819) (#6819) Add an extension method to generate an absolute URL for a page. --- .../UrlHelperExtensions.cs | 52 +++++++ .../Routing/UrlHelperExtensionsTest.cs | 145 ++++++++++++++++++ 2 files changed, 197 insertions(+) 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)