From 649ee4d28d7cf46401c09ad0108a4f23b6d7d513 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 24 Apr 2017 15:02:07 -0700 Subject: [PATCH] Add support for page handler to URL generating APIs. --- .../ControllerBase.cs | 69 ++++++++++++-- .../Internal/RedirectToPageResultExecutor.cs | 1 + .../RedirectToPageResult.cs | 53 +++++++++-- .../UrlHelperExtensions.cs | 59 +++++++++--- .../Page.cs | 70 ++++++++++---- .../PageModel.cs | 94 +++++++++++++++---- .../AnchorTagHelper.cs | 21 ++++- .../FormActionTagHelper.cs | 26 ++++- .../FormTagHelper.cs | 14 ++- .../Properties/Resources.Designer.cs | 24 ++--- .../Resources.resx | 6 +- .../ViewFeatures/DefaultHtmlGenerator.cs | 6 +- .../ViewFeatures/IHtmlGenerator.cs | 6 +- .../ControllerBaseTest.cs | 4 +- .../RedirectToPageResultTest.cs | 9 +- .../Routing/UrlHelperTest.cs | 63 +++++++++++-- .../RazorPagesWithBasePathTest.cs | 11 ++- .../PageModelTest.cs | 4 +- .../PageTest.cs | 4 +- .../AnchorTagHelperTest.cs | 11 ++- .../FormActionTagHelperTest.cs | 46 ++++++++- .../FormTagHelperTest.cs | 5 +- .../Pages/TagHelper/CrossPost.cshtml | 5 +- .../Pages/TagHelper/FormAction.cshtml | 5 +- .../Pages/TagHelper/PostWithHandler.cshtml | 16 ++++ 25 files changed, 507 insertions(+), 125 deletions(-) create mode 100644 test/WebSites/RazorPagesWebSite/Pages/TagHelper/PostWithHandler.cshtml diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ControllerBase.cs b/src/Microsoft.AspNetCore.Mvc.Core/ControllerBase.cs index 49826cb116..305dfe33e9 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ControllerBase.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ControllerBase.cs @@ -961,30 +961,54 @@ namespace Microsoft.AspNetCore.Mvc /// The . [NonAction] public virtual RedirectToPageResult RedirectToPage(string pageName, object routeValues) - => RedirectToPage(pageName, routeValues, fragment: null); + => RedirectToPage(pageName, pageHandler: null, routeValues: routeValues, fragment: null); + + /// + /// Redirects () to the specified + /// using the specified . + /// + /// The name of the page. + /// The page handler to redirect to. + /// The . + [NonAction] + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler) + => RedirectToPage(pageName, pageHandler, routeValues: null); + + /// + /// Redirects () to the specified . + /// + /// The name of the page. + /// The page handler to redirect to. + /// The parameters for a route. + /// The . + [NonAction] + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler, object routeValues) + => RedirectToPage(pageName, pageHandler, routeValues); /// /// Redirects () to the specified /// using the specified . /// /// The name of the page. + /// The page handler to redirect to. /// The fragment to add to the URL. /// The . [NonAction] - public virtual RedirectToPageResult RedirectToPage(string pageName, string fragment) - => RedirectToPage(pageName, routeValues: null, fragment: fragment); + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler, string fragment) + => RedirectToPage(pageName, pageHandler, routeValues: null, fragment: fragment); /// /// Redirects () to the specified /// using the specified and . /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for a route. /// The fragment to add to the URL. /// The . [NonAction] - public virtual RedirectToPageResult RedirectToPage(string pageName, object routeValues, string fragment) - => new RedirectToPageResult(pageName, routeValues, fragment); + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler, object routeValues, string fragment) + => new RedirectToPageResult(pageName, pageHandler, routeValues, fragment); /// /// Redirects () to the specified . @@ -1004,30 +1028,49 @@ namespace Microsoft.AspNetCore.Mvc /// The with set. [NonAction] public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, object routeValues) - => RedirectToPagePermanent(pageName, routeValues, fragment: null); + => RedirectToPagePermanent(pageName, pageHandler: null, routeValues: routeValues, fragment: null); + + /// + /// Redirects () to the specified + /// using the specified . + /// + /// The name of the page. + /// The page handler to redirect to. + /// The with set. + [NonAction] + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string pageHandler) + => RedirectToPagePermanent(pageName, routeValues: null); /// /// Redirects () to the specified /// using the specified . /// /// The name of the page. + /// The page handler to redirect to. /// The fragment to add to the URL. /// The with set. [NonAction] - public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string fragment) - => RedirectToPagePermanent(pageName, routeValues: null, fragment: fragment); + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string pageHandler, string fragment) + => RedirectToPagePermanent(pageName, pageHandler, routeValues: null, fragment: fragment); /// /// Redirects () to the specified /// using the specified and . /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for a route. /// The fragment to add to the URL. /// The with set. [NonAction] - public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, object routeValues, string fragment) - => new RedirectToPageResult(pageName, routeValues, permanent: true, fragment: fragment); + public virtual RedirectToPageResult RedirectToPagePermanent( + string pageName, + string pageHandler, + object routeValues, + string fragment) + { + return new RedirectToPageResult(pageName, pageHandler, routeValues, permanent: true, fragment: fragment); + } /// /// Redirects () to the specified page with @@ -1035,12 +1078,14 @@ namespace Microsoft.AspNetCore.Mvc /// set to true, using the specified , , and . /// /// The name of the page. + /// The page handler to redirect to. /// The route data to use for generating the URL. /// The fragment to add to the URL. /// The created for the response. [NonAction] public virtual RedirectToPageResult RedirectToPagePreserveMethod( string pageName, + string pageHandler = null, object routeValues = null, string fragment = null) { @@ -1051,6 +1096,7 @@ namespace Microsoft.AspNetCore.Mvc return new RedirectToPageResult( pageName: pageName, + pageHandler: pageHandler, routeValues: routeValues, permanent: false, preserveMethod: true, @@ -1063,12 +1109,14 @@ namespace Microsoft.AspNetCore.Mvc /// set to true, using the specified , , and . /// /// The name of the page. + /// The page handler to redirect to. /// The route data to use for generating the URL. /// The fragment to add to the URL. /// The created for the response. [NonAction] public virtual RedirectToPageResult RedirectToPagePermanentPreserveMethod( string pageName, + string pageHandler = null, object routeValues = null, string fragment = null) { @@ -1079,6 +1127,7 @@ namespace Microsoft.AspNetCore.Mvc return new RedirectToPageResult( pageName: pageName, + pageHandler: pageHandler, routeValues: routeValues, permanent: true, preserveMethod: true, diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/RedirectToPageResultExecutor.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/RedirectToPageResultExecutor.cs index 87f3b15e04..88b482d5a2 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/RedirectToPageResultExecutor.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/RedirectToPageResultExecutor.cs @@ -36,6 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var urlHelper = result.UrlHelper ?? _urlHelperFactory.GetUrlHelper(context); var destinationUrl = urlHelper.Page( result.PageName, + result.PageHandler, result.RouteValues, result.Protocol, result.Host, diff --git a/src/Microsoft.AspNetCore.Mvc.Core/RedirectToPageResult.cs b/src/Microsoft.AspNetCore.Mvc.Core/RedirectToPageResult.cs index ae5198ae1e..319166f338 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/RedirectToPageResult.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/RedirectToPageResult.cs @@ -26,6 +26,17 @@ namespace Microsoft.AspNetCore.Mvc { } + /// + /// Initializes a new instance of the with the values + /// provided. + /// + /// The page to redirect to. + /// The page handler to redirect to. + public RedirectToPageResult(string pageName, string pageHandler) + : this(pageName, pageHandler, routeValues: null) + { + } + /// /// Initializes a new instance of the with the values /// provided. @@ -33,7 +44,19 @@ namespace Microsoft.AspNetCore.Mvc /// The page to redirect to. /// The parameters for the route. public RedirectToPageResult(string pageName, object routeValues) - : this(pageName, routeValues, permanent: false) + : this(pageName, pageHandler: null, routeValues: routeValues, permanent: false) + { + } + + /// + /// Initializes a new instance of the with the values + /// provided. + /// + /// The page to redirect to. + /// The page handler to redirect to. + /// The parameters for the route. + public RedirectToPageResult(string pageName, string pageHandler, object routeValues) + : this(pageName, pageHandler, routeValues, permanent: false) { } @@ -42,13 +65,15 @@ namespace Microsoft.AspNetCore.Mvc /// provided. /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for the page. /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). public RedirectToPageResult( string pageName, + string pageHandler, object routeValues, bool permanent) - : this(pageName, routeValues, permanent, fragment: null) + : this(pageName, pageHandler, routeValues, permanent, fragment: null) { } @@ -56,15 +81,17 @@ namespace Microsoft.AspNetCore.Mvc /// Initializes a new instance of the with the values provided. /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for the page. /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the intial request method. public RedirectToPageResult( string pageName, + string pageHandler, object routeValues, bool permanent, bool preserveMethod) - : this(pageName, routeValues, permanent, preserveMethod, fragment: null) + : this(pageName, pageHandler, routeValues, permanent, preserveMethod, fragment: null) { } @@ -73,13 +100,15 @@ namespace Microsoft.AspNetCore.Mvc /// provided. /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for the route. /// The fragment to add to the URL. public RedirectToPageResult( string pageName, + string pageHandler, object routeValues, string fragment) - : this(pageName, routeValues, permanent: false, fragment: fragment) + : this(pageName, pageHandler, routeValues, permanent: false, fragment: fragment) { } @@ -88,19 +117,19 @@ namespace Microsoft.AspNetCore.Mvc /// provided. /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for the page. /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). /// The fragment to add to the URL. public RedirectToPageResult( string pageName, + string pageHandler, object routeValues, bool permanent, string fragment) + : this(pageName, pageHandler, routeValues, permanent, preserveMethod: false, fragment: fragment) { - PageName = pageName; - RouteValues = routeValues == null ? new RouteValueDictionary() : new RouteValueDictionary(routeValues); - Permanent = permanent; - Fragment = fragment; + } /// @@ -108,18 +137,21 @@ namespace Microsoft.AspNetCore.Mvc /// provided. /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for the page. /// If set to true, makes the redirect permanent (301). Otherwise a temporary redirect is used (302). /// If set to true, make the temporary redirect (307) or permanent redirect (308) preserve the intial request method. /// The fragment to add to the URL. public RedirectToPageResult( string pageName, + string pageHandler, object routeValues, bool permanent, bool preserveMethod, string fragment) { PageName = pageName; + PageHandler = pageHandler; RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues); PreserveMethod = preserveMethod; Permanent = permanent; @@ -136,6 +168,11 @@ namespace Microsoft.AspNetCore.Mvc /// public string PageName { get; set; } + /// + /// Gets or sets the the page handler to redirect to. + /// + public string PageHandler { get; set; } + /// /// Gets or sets the route data to use for generating the URL. /// diff --git a/src/Microsoft.AspNetCore.Mvc.Core/UrlHelperExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Core/UrlHelperExtensions.cs index 391acd9053..d7835c28a3 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/UrlHelperExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/UrlHelperExtensions.cs @@ -351,13 +351,10 @@ namespace Microsoft.AspNetCore.Mvc /// /// The . /// The page name to generate the url for. - /// An object that contains route values. + /// The handler to generate the url for. /// The generated URL. - public static string Page( - this IUrlHelper urlHelper, - string pageName, - object values) - => Page(urlHelper, pageName, values, protocol: null); + public static string Page(this IUrlHelper urlHelper, string pageName, string pageHandler) + => Page(urlHelper, pageName, pageHandler, values: null); /// /// Generates a URL with an absolute path for the specified . @@ -365,20 +362,48 @@ namespace Microsoft.AspNetCore.Mvc /// The . /// The page name to generate the url for. /// An object that contains route values. + /// The generated URL. + public static string Page(this IUrlHelper urlHelper, string pageName, object values) + => Page(urlHelper, pageName, pageHandler: null, values: values); + + /// + /// Generates a URL with an absolute path for the specified . + /// + /// The . + /// The page name to generate the url for. + /// The handler to generate the url for. + /// An object that contains route values. + /// The generated URL. + public static string Page( + this IUrlHelper urlHelper, + string pageName, + string pageHandler, + object values) + => Page(urlHelper, pageName, pageHandler, values, protocol: null); + + /// + /// Generates a URL with an absolute path for the specified . + /// + /// The . + /// The page name to generate the url for. + /// The handler to generate the url for. + /// An object that contains route values. /// The protocol for the URL, such as "http" or "https". /// The generated URL. public static string Page( this IUrlHelper urlHelper, string pageName, + string pageHandler, object values, string protocol) - => Page(urlHelper, pageName, values, protocol, host: null, fragment: null); + => Page(urlHelper, pageName, pageHandler, values, protocol, host: null, fragment: null); /// /// Generates a URL with an absolute path for the specified . /// /// The . /// The page name to generate the url for. + /// The handler to generate the url for. /// An object that contains route values. /// The protocol for the URL, such as "http" or "https". /// The host name for the URL. @@ -386,16 +411,18 @@ namespace Microsoft.AspNetCore.Mvc public static string Page( this IUrlHelper urlHelper, string pageName, + string pageHandler, object values, string protocol, string host) - => Page(urlHelper, pageName, values, protocol, host, fragment: null); + => Page(urlHelper, pageName, pageHandler, values, protocol, host, fragment: null); /// /// Generates a URL with an absolute path for the specified . /// /// The . /// The page name to generate the url for. + /// The handler to generate the url for. /// An object that contains route values. /// The protocol for the URL, such as "http" or "https". /// The host name for the URL. @@ -404,6 +431,7 @@ namespace Microsoft.AspNetCore.Mvc public static string Page( this IUrlHelper urlHelper, string pageName, + string pageHandler, object values, string protocol, string host, @@ -429,11 +457,18 @@ namespace Microsoft.AspNetCore.Mvc routeValues["page"] = CalculatePageName(urlHelper.ActionContext, pageName); } - if (!routeValues.ContainsKey("handler") && - ambientValues.TryGetValue("handler", out var handler)) + if (string.IsNullOrEmpty(pageHandler)) { - // Clear out handler unless it's explicitly specified in the routeValues. - routeValues["handler"] = null; + if (!routeValues.ContainsKey("handler") && + ambientValues.TryGetValue("handler", out var handler)) + { + // Clear out formaction unless it's explicitly specified in the routeValues. + routeValues["handler"] = null; + } + } + else + { + routeValues["handler"] = pageHandler; } return urlHelper.RouteUrl( diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs index f90b3365d4..f7c25b428b 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs @@ -160,7 +160,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages } } - #region Factory methods /// /// Creates a . /// @@ -970,6 +969,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages public virtual RedirectToPageResult RedirectToPage(string pageName) => RedirectToPage(pageName, routeValues: null); + /// + /// Redirects () to the specified + /// using the specified . + /// + /// The name of the page. + /// The page handler to redirect to. + /// The . + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler) + => RedirectToPage(pageName, routeValues: null); + /// /// Redirects () to the specified /// using the specified . @@ -978,28 +987,30 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// The parameters for a route. /// The . public virtual RedirectToPageResult RedirectToPage(string pageName, object routeValues) - => RedirectToPage(pageName, routeValues, fragment: null); + => RedirectToPage(pageName, pageHandler: null, routeValues: routeValues, fragment: null); /// /// Redirects () to the specified /// using the specified . /// /// The name of the page. + /// The page handler to redirect to. /// The fragment to add to the URL. /// The . - public virtual RedirectToPageResult RedirectToPage(string pageName, string fragment) - => RedirectToPage(pageName, routeValues: null, fragment: fragment); + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler, string fragment) + => RedirectToPage(pageName, pageHandler, routeValues: null, fragment: fragment); /// /// Redirects () to the specified /// using the specified and . /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for a route. /// The fragment to add to the URL. /// The . - public virtual RedirectToPageResult RedirectToPage(string pageName, object routeValues, string fragment) - => new RedirectToPageResult(pageName, routeValues, fragment); + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler, object routeValues, string fragment) + => new RedirectToPageResult(pageName, pageHandler, routeValues, fragment); /// /// Redirects () to the specified . @@ -1007,7 +1018,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// The name of the page. /// The with set. public virtual RedirectToPageResult RedirectToPagePermanent(string pageName) - => RedirectToPagePermanent(pageName, routeValues: null); + => RedirectToPagePermanent(pageName, pageHandler: null); /// /// Redirects () to the specified @@ -1017,28 +1028,51 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// The parameters for a route. /// The with set. public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, object routeValues) - => RedirectToPagePermanent(pageName, routeValues, fragment: null); + => RedirectToPagePermanent(pageName, pageHandler: null, routeValues: routeValues, fragment: null); + + /// + /// Redirects () to the specified + /// using the specified . + /// + /// The name of the page. + /// The page handler to redirect to. + /// The with set. + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string pageHandler) + => RedirectToPagePermanent(pageName, pageHandler, routeValues: null); + + /// + /// Redirects () to the specified + /// using the specified . + /// + /// The name of the page. + /// The page handler to redirect to. + /// The parameters for a route. + /// The with set. + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string pageHandler, object routeValues) + => RedirectToPagePermanent(pageName, pageHandler, routeValues, fragment: null); /// /// Redirects () to the specified /// using the specified . /// /// The name of the page. + /// The page handler to redirect to. /// The fragment to add to the URL. /// The with set. - public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string fragment) - => RedirectToPagePermanent(pageName, routeValues: null, fragment: fragment); + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string pageHandler, string fragment) + => RedirectToPagePermanent(pageName, pageHandler, routeValues: null, fragment: fragment); /// /// Redirects () to the specified /// using the specified and . /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for a route. /// The fragment to add to the URL. /// The with set. - protected RedirectToPageResult RedirectToPagePermanent(string pageName, object routeValues, string fragment) - => new RedirectToPageResult(pageName, routeValues, permanent: true, fragment: fragment); + protected RedirectToPageResult RedirectToPagePermanent(string pageName, string pageHandler, object routeValues, string fragment) + => new RedirectToPageResult(pageName, pageHandler, routeValues, permanent: true, fragment: fragment); /// /// Redirects () to the specified page with @@ -1046,16 +1080,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// set to true, using the specified , , and . /// /// The name of the page. + /// The page handler to redirect to. /// The route data to use for generating the URL. /// The fragment to add to the URL. /// The created for the response. public virtual RedirectToPageResult RedirectToPagePreserveMethod( string pageName = null, + string pageHandler = null, object routeValues = null, string fragment = null) { return new RedirectToPageResult( pageName: pageName, + pageHandler: pageHandler, routeValues: routeValues, permanent: false, preserveMethod: true, @@ -1068,16 +1105,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// set to true, using the specified , , and . /// /// The name of the page. + /// The page handler to redirect to. /// The route data to use for generating the URL. /// The fragment to add to the URL. /// The created for the response. public virtual RedirectToPageResult RedirectToPagePermanentPreserveMethod( string pageName = null, + string pageHandler = null, object routeValues = null, string fragment = null) { return new RedirectToPageResult( pageName: pageName, + pageHandler: pageHandler, routeValues: routeValues, permanent: true, preserveMethod: true, @@ -1157,11 +1197,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// Returning a from a page handler method is equivalent to returning void. /// The view associated with the page will be executed. /// - protected PageViewResult View() - { - return new PageViewResult(this); - } - #endregion Factory methods + protected PageViewResult View() => new PageViewResult(this); /// /// Updates the specified instance using values from the 's current diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs index 54ec258e24..187c97fce6 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs @@ -488,7 +488,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages propertyFilter); } - #region Factory methods /// /// Creates a . /// @@ -1298,9 +1297,30 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// /// The name of the page. /// The . - protected internal RedirectToPageResult RedirectToPage(string pageName) + public virtual RedirectToPageResult RedirectToPage(string pageName) => RedirectToPage(pageName, routeValues: null); + /// + /// Redirects () to the specified + /// using the specified . + /// + /// The name of the page. + /// The page handler to redirect to. + /// The . + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler) + => RedirectToPage(pageName, pageHandler, routeValues: null); + + /// + /// Redirects () to the specified + /// using the specified and . + /// + /// The name of the page. + /// The page handler to redirect to. + /// The parameters for a route. + /// The . + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler, object routeValues) + => RedirectToPage(pageName, pageHandler, routeValues, fragment: null); + /// /// Redirects () to the specified /// using the specified . @@ -1308,37 +1328,39 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// The name of the page. /// The parameters for a route. /// The . - protected internal RedirectToPageResult RedirectToPage(string pageName, object routeValues) - => RedirectToPage(pageName, routeValues, fragment: null); + public virtual RedirectToPageResult RedirectToPage(string pageName, object routeValues) + => RedirectToPage(pageName, pageHandler: null, routeValues: routeValues, fragment: null); /// /// Redirects () to the specified /// using the specified . /// /// The name of the page. + /// The page handler to redirect to. /// The fragment to add to the URL. /// The . - protected internal RedirectToPageResult RedirectToPage(string pageName, string fragment) - => RedirectToPage(pageName, routeValues: null, fragment: fragment); + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler, string fragment) + => RedirectToPage(pageName, pageHandler, routeValues: null, fragment: fragment); /// /// Redirects () to the specified /// using the specified and . /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for a route. /// The fragment to add to the URL. /// The . - protected internal RedirectToPageResult RedirectToPage(string pageName, object routeValues, string fragment) - => new RedirectToPageResult(pageName, routeValues, fragment); + public virtual RedirectToPageResult RedirectToPage(string pageName, string pageHandler, object routeValues, string fragment) + => new RedirectToPageResult(pageName, pageHandler, routeValues, fragment); /// /// Redirects () to the specified . /// /// The name of the page. /// The with set. - protected internal RedirectToPageResult RedirectToPagePermanent(string pageName) - => RedirectToPagePermanent(pageName, routeValues: null); + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName) + => RedirectToPagePermanent(pageName, pageHandler: null); /// /// Redirects () to the specified @@ -1347,29 +1369,62 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// The name of the page. /// The parameters for a route. /// The with set. - protected internal RedirectToPageResult RedirectToPagePermanent(string pageName, object routeValues) - => RedirectToPagePermanent(pageName, routeValues, fragment: null); + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, object routeValues) + => RedirectToPagePermanent(pageName, pageHandler: null, routeValues: routeValues, fragment: null); + + /// + /// Redirects () to the specified . + /// + /// The name of the page. + /// The page handler to redirect to. + /// The with set. + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string pageHandler) + => RedirectToPagePermanent(pageName, pageHandler, routeValues: null); + + /// + /// Redirects () to the specified + /// using the specified . + /// + /// The name of the page. + /// The page handler to redirect to. + /// The parameters for a route. + /// The with set. + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string pageHandler, object routeValues) + => RedirectToPagePermanent(pageName, pageHandler, routeValues, fragment: null); /// /// Redirects () to the specified /// using the specified . /// /// The name of the page. + /// The page handler to redirect to. /// The fragment to add to the URL. /// The with set. - protected internal RedirectToPageResult RedirectToPagePermanent(string pageName, string fragment) - => RedirectToPagePermanent(pageName, routeValues: null, fragment: fragment); + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string pageHandler, string fragment) + => RedirectToPagePermanent(pageName, pageHandler, routeValues: null, fragment: fragment); + + /// + /// Redirects () to the specified + /// using the specified . + /// + /// The name of the page. + /// The parameters for a route. + /// The fragment to add to the URL. + /// The with set. + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, object routeValues, string fragment) + => RedirectToPagePermanent(pageName, pageHandler: null, routeValues: routeValues, fragment: fragment); /// /// Redirects () to the specified /// using the specified and . /// /// The name of the page. + /// The page handler to redirect to. /// The parameters for a route. /// The fragment to add to the URL. /// The with set. - protected internal RedirectToPageResult RedirectToPagePermanent(string pageName, object routeValues, string fragment) - => new RedirectToPageResult(pageName, routeValues, permanent: true, fragment: fragment); + public virtual RedirectToPageResult RedirectToPagePermanent(string pageName, string pageHandler, object routeValues, string fragment) + => new RedirectToPageResult(pageName, pageHandler, routeValues, permanent: true, fragment: fragment); /// /// Redirects () to the specified page with @@ -1377,16 +1432,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// set to true, using the specified , , and . /// /// The name of the page. + /// The page handler to redirect to. /// The route data to use for generating the URL. /// The fragment to add to the URL. /// The created for the response. public virtual RedirectToPageResult RedirectToPagePreserveMethod( string pageName = null, + string pageHandler = null, object routeValues = null, string fragment = null) { return new RedirectToPageResult( pageName: pageName, + pageHandler: pageHandler, routeValues: routeValues, permanent: false, preserveMethod: true, @@ -1399,16 +1457,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// set to true, using the specified , , and . /// /// The name of the page. + /// The page handler to redirect to. /// The route data to use for generating the URL. /// The fragment to add to the URL. /// The created for the response. public virtual RedirectToPageResult RedirectToPagePermanentPreserveMethod( string pageName = null, + string pageHandler = null, object routeValues = null, string fragment = null) { return new RedirectToPageResult( pageName: pageName, + pageHandler: pageHandler, routeValues: routeValues, permanent: true, preserveMethod: true, @@ -1493,7 +1554,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages { return new PageViewResult(Page); } - #endregion Factory methods /// /// Validates the specified instance. diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/AnchorTagHelper.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/AnchorTagHelper.cs index d7de3ea208..17be080c6b 100644 --- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/AnchorTagHelper.cs +++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/AnchorTagHelper.cs @@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers [HtmlTargetElement("a", Attributes = ControllerAttributeName)] [HtmlTargetElement("a", Attributes = AreaAttributeName)] [HtmlTargetElement("a", Attributes = PageAttributeName)] + [HtmlTargetElement("a", Attributes = PageHandlerAttributeName)] [HtmlTargetElement("a", Attributes = FragmentAttributeName)] [HtmlTargetElement("a", Attributes = HostAttributeName)] [HtmlTargetElement("a", Attributes = ProtocolAttributeName)] @@ -29,6 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers private const string ControllerAttributeName = "asp-controller"; private const string AreaAttributeName = "asp-area"; private const string PageAttributeName = "asp-page"; + private const string PageHandlerAttributeName = "asp-page-handler"; private const string FragmentAttributeName = "asp-fragment"; private const string HostAttributeName = "asp-host"; private const string ProtocolAttributeName = "asp-protocol"; @@ -95,6 +97,16 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers [HtmlAttributeName(PageAttributeName)] public string Page { get; set; } + /// + /// The name of the page handler. + /// + /// + /// Must be null if or , or + /// is non-null. + /// + [HtmlAttributeName(PageHandlerAttributeName)] + public string PageHandler { get; set; } + /// /// The protocol for the URL, such as "http" or "https". /// @@ -172,6 +184,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers Controller != null || Area != null || Page != null || + PageHandler != null || Route != null || Protocol != null || Host != null || @@ -191,7 +204,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers ProtocolAttributeName, HostAttributeName, FragmentAttributeName, - PageAttributeName)); + PageAttributeName, + PageHandlerAttributeName)); } return; @@ -199,7 +213,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers var routeLink = Route != null; var actionLink = Controller != null || Action != null; - var pageLink = Page != null; + var pageLink = Page != null || PageHandler != null; if ((routeLink && actionLink) || (routeLink && pageLink) || (actionLink && pageLink)) { @@ -208,7 +222,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers Resources.FormatCannotDetermineAttributeFor(Href, ""), RouteAttributeName, ControllerAttributeName + ", " + ActionAttributeName, - PageAttributeName); + PageAttributeName + ", " + PageHandlerAttributeName); throw new InvalidOperationException(message); } @@ -236,6 +250,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers ViewContext, linkText: string.Empty, pageName: Page, + pageHandler: PageHandler, protocol: Protocol, hostname: Host, fragment: Fragment, diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormActionTagHelper.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormActionTagHelper.cs index 5c2b8201ca..18c89f37e4 100644 --- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormActionTagHelper.cs +++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormActionTagHelper.cs @@ -19,6 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers [HtmlTargetElement("button", Attributes = ControllerAttributeName)] [HtmlTargetElement("button", Attributes = AreaAttributeName)] [HtmlTargetElement("button", Attributes = PageAttributeName)] + [HtmlTargetElement("button", Attributes = PageHandlerAttributeName)] [HtmlTargetElement("button", Attributes = FragmentAttributeName)] [HtmlTargetElement("button", Attributes = RouteAttributeName)] [HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)] @@ -27,6 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers [HtmlTargetElement("input", Attributes = ImageControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImagePageAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] + [HtmlTargetElement("input", Attributes = ImagePageHandlerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageFragmentAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)] @@ -35,6 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers [HtmlTargetElement("input", Attributes = SubmitControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitPageAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] + [HtmlTargetElement("input", Attributes = SubmitPageHandlerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitFragmentAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)] @@ -45,6 +48,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers private const string AreaAttributeName = "asp-area"; private const string ControllerAttributeName = "asp-controller"; private const string PageAttributeName = "asp-page"; + private const string PageHandlerAttributeName = "asp-page-handler"; private const string FragmentAttributeName = "asp-fragment"; private const string RouteAttributeName = "asp-route"; private const string RouteValuesDictionaryName = "asp-all-route-data"; @@ -55,6 +59,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers private const string ImageActionAttributeSelector = ImageTypeSelector + ActionAttributeName; private const string ImageAreaAttributeSelector = ImageTypeSelector + AreaAttributeName; private const string ImagePageAttributeSelector = ImageTypeSelector + PageAttributeName; + private const string ImagePageHandlerAttributeSelector = ImageTypeSelector + PageHandlerAttributeName; private const string ImageFragmentAttributeSelector = ImageTypeSelector + FragmentAttributeName; private const string ImageControllerAttributeSelector = ImageTypeSelector + ControllerAttributeName; private const string ImageRouteAttributeSelector = ImageTypeSelector + RouteAttributeName; @@ -65,6 +70,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers private const string SubmitActionAttributeSelector = SubmitTypeSelector + ActionAttributeName; private const string SubmitAreaAttributeSelector = SubmitTypeSelector + AreaAttributeName; private const string SubmitPageAttributeSelector = SubmitTypeSelector + PageAttributeName; + private const string SubmitPageHandlerAttributeSelector = SubmitTypeSelector + PageHandlerAttributeName; private const string SubmitFragmentAttributeSelector = SubmitTypeSelector + FragmentAttributeName; private const string SubmitControllerAttributeSelector = SubmitTypeSelector + ControllerAttributeName; private const string SubmitRouteAttributeSelector = SubmitTypeSelector + RouteAttributeName; @@ -118,6 +124,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers [HtmlAttributeName(PageAttributeName)] public string Page { get; set; } + /// + /// The name of the page handler. + /// + [HtmlAttributeName(PageHandlerAttributeName)] + public string PageHandler { get; set; } + /// /// Gets or sets the URL fragment. /// @@ -180,6 +192,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers if (Action != null || Controller != null || Area != null || + Page != null || + PageHandler != null || Fragment != null || Route != null || (_routeValues != null && _routeValues.Count > 0)) @@ -188,14 +202,16 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers // attribute. throw new InvalidOperationException( Resources.FormatFormActionTagHelper_CannotOverrideFormAction( + FormAction, output.TagName, + RouteValuesPrefix, ActionAttributeName, ControllerAttributeName, AreaAttributeName, FragmentAttributeName, RouteAttributeName, - RouteValuesPrefix, - FormAction)); + PageAttributeName, + PageHandlerAttributeName)); } return; @@ -203,7 +219,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers var routeLink = Route != null; var actionLink = Controller != null || Action != null; - var pageLink = Page != null; + var pageLink = Page != null || PageHandler != null; if ((routeLink && actionLink) || (routeLink && pageLink) || (actionLink && pageLink)) { @@ -212,7 +228,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers Resources.FormatCannotDetermineAttributeFor(FormAction, '<' + output.TagName + '>'), RouteAttributeName, ControllerAttributeName + ", " + ActionAttributeName, - PageAttributeName); + PageAttributeName + ", " + PageHandlerAttributeName); throw new InvalidOperationException(message); } @@ -238,7 +254,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers string url; if (pageLink) { - url = urlHelper.Page(Page, routeValues, protocol: null, host: null, fragment: Fragment); + url = urlHelper.Page(Page, PageHandler, routeValues, protocol: null, host: null, fragment: Fragment); } else if (routeLink) { diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs index d9907d99fc..44622b4aa2 100644 --- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs +++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs @@ -22,6 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers private const string AntiforgeryAttributeName = "asp-antiforgery"; private const string AreaAttributeName = "asp-area"; private const string PageAttributeName = "asp-page"; + private const string PageHandlerAttributeName = "asp-page-handler"; private const string FragmentAttributeName = "asp-fragment"; private const string ControllerAttributeName = "asp-controller"; private const string RouteAttributeName = "asp-route"; @@ -74,6 +75,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers [HtmlAttributeName(PageAttributeName)] public string Page { get; set; } + /// + /// The name of the page handler. + /// + [HtmlAttributeName(PageHandlerAttributeName)] + public string PageHandler { get; set; } + /// /// Whether the antiforgery token should be generated. /// @@ -164,6 +171,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers Controller != null || Area != null || Page != null || + PageHandler != null || Fragment != null || Route != null || (_routeValues != null && _routeValues.Count > 0)) @@ -179,7 +187,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers FragmentAttributeName, AreaAttributeName, RouteAttributeName, - PageAttributeName)); + PageAttributeName, + PageHandlerAttributeName)); } string attributeValue = null; @@ -210,7 +219,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers { var routeLink = Route != null; var actionLink = Controller != null || Action != null; - var pageLink = Page != null; + var pageLink = Page != null || PageHandler != null; if ((routeLink && actionLink) || (routeLink && pageLink) || (actionLink && pageLink)) { @@ -259,6 +268,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers tagBuilder = Generator.GeneratePageForm( ViewContext, Page, + PageHandler, routeValues, Fragment, method: null, diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/Properties/Resources.Designer.cs index 64b24c6483..75849c6923 100644 --- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/Properties/Resources.Designer.cs @@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers => string.Format(CultureInfo.CurrentCulture, GetString("CannotDetermineAttributeFor"), p0, p1); /// - /// Cannot override the '{0}' attribute for {1}. An {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}', or '{10}' attribute. + /// Cannot override the '{0}' attribute for {1}. An {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}', '{10}' or '{11}' attribute. /// internal static string AnchorTagHelper_CannotOverrideHref { @@ -33,13 +33,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers } /// - /// Cannot override the '{0}' attribute for {1}. An {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}', or '{10}' attribute. + /// Cannot override the '{0}' attribute for {1}. An {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}', '{10}' or '{11}' attribute. /// - internal static string FormatAnchorTagHelper_CannotOverrideHref(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7, object p8, object p9, object p10) - => string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotOverrideHref"), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); + internal static string FormatAnchorTagHelper_CannotOverrideHref(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7, object p8, object p9, object p10, object p11) + => string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotOverrideHref"), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); /// - /// Cannot override the '{0}' attribute for {1}. A {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}' or '{8}' attribute. + /// Cannot override the '{0}' attribute for {1}. A {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}' or '{9}' attribute. /// internal static string FormTagHelper_CannotOverrideAction { @@ -47,10 +47,10 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers } /// - /// Cannot override the '{0}' attribute for {1}. A {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}' or '{8}' attribute. + /// Cannot override the '{0}' attribute for {1}. A {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}' or '{9}' attribute. /// - internal static string FormatFormTagHelper_CannotOverrideAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7, object p8) - => string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotOverrideAction"), p0, p1, p2, p3, p4, p5, p6, p7, p8); + internal static string FormatFormTagHelper_CannotOverrideAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7, object p8, object p9) + => string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotOverrideAction"), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); /// /// Unexpected '{1}' expression result type '{2}' for {0}. '{1}' must be of type '{3}' or '{4}' that can be parsed as a '{3}' if '{5}' is '{6}'. @@ -151,7 +151,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers => string.Format(CultureInfo.CurrentCulture, GetString("PropertyOfTypeCannotBeNull"), p0, p1); /// - /// Cannot override the '{7}' attribute for <{0}>. <{0}> elements with a specified '{7}' must not have attributes starting with '{6}' or an '{1}', '{2}', '{3}', '{4}', or '{5}' attribute. + /// Cannot override the '{0}' attribute for <{1}>. <{1}> elements with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}' or '{9}' attribute. /// internal static string FormActionTagHelper_CannotOverrideFormAction { @@ -159,10 +159,10 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers } /// - /// Cannot override the '{7}' attribute for <{0}>. <{0}> elements with a specified '{7}' must not have attributes starting with '{6}' or an '{1}', '{2}', '{3}', '{4}', or '{5}' attribute. + /// Cannot override the '{0}' attribute for <{1}>. <{1}> elements with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}' or '{9}' attribute. /// - internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7) - => string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6, p7); + internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7, object p8, object p9) + => string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); private static string GetString(string name, params string[] formatterNames) { diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/Resources.resx b/src/Microsoft.AspNetCore.Mvc.TagHelpers/Resources.resx index b41c161bcd..9ef50b9953 100644 --- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/Resources.resx +++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/Resources.resx @@ -121,10 +121,10 @@ Cannot determine the '{0}' attribute for {1}. The following attributes are mutually exclusive: - Cannot override the '{0}' attribute for {1}. An {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}', or '{10}' attribute. + Cannot override the '{0}' attribute for {1}. An {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}', '{10}' or '{11}' attribute. - Cannot override the '{0}' attribute for {1}. A {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}' or '{8}' attribute. + Cannot override the '{0}' attribute for {1}. A {1} with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}' or '{9}' attribute. Unexpected '{1}' expression result type '{2}' for {0}. '{1}' must be of type '{3}' or '{4}' that can be parsed as a '{3}' if '{5}' is '{6}'. @@ -148,6 +148,6 @@ The '{0}' property of '{1}' must not be null. - Cannot override the '{7}' attribute for <{0}>. <{0}> elements with a specified '{7}' must not have attributes starting with '{6}' or an '{1}', '{2}', '{3}', '{4}', or '{5}' attribute. + Cannot override the '{0}' attribute for <{1}>. <{1}> elements with a specified '{0}' must not have attributes starting with '{2}' or an '{3}', '{4}', '{5}', '{6}', '{7}', '{8}' or '{9}' attribute. \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs index f97cb2fc35..94ac30803a 100644 --- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs @@ -147,6 +147,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures ViewContext viewContext, string linkText, string pageName, + string pageHandler, string protocol, string hostname, string fragment, @@ -164,7 +165,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures } var urlHelper = _urlHelperFactory.GetUrlHelper(viewContext); - var url = urlHelper.Page(pageName, routeValues, protocol, hostname, fragment); + var url = urlHelper.Page(pageName, pageHandler, routeValues, protocol, hostname, fragment); return GenerateLink(linkText, url, htmlAttributes); } @@ -311,6 +312,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures public virtual TagBuilder GeneratePageForm( ViewContext viewContext, string pageName, + string pageHandler, object routeValues, string fragment, string method, @@ -347,7 +349,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures else { var urlHelper = _urlHelperFactory.GetUrlHelper(viewContext); - action = urlHelper.Page(pageName, values: routeValues,protocol: null, host: null, fragment: fragment); + action = urlHelper.Page(pageName, pageHandler, routeValues, protocol: null, host: null, fragment: fragment); } return GenerateFormCore(viewContext, action, method, htmlAttributes); diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/IHtmlGenerator.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/IHtmlGenerator.cs index 19f5dcfa20..e492f9bf5d 100644 --- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/IHtmlGenerator.cs +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/IHtmlGenerator.cs @@ -60,6 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures /// The instance for the current scope. /// The text to insert inside the element. /// The page name. + /// The page handler. /// The protocol (scheme) for the generated link. /// The hostname for the generated link. /// The fragment for the genrated link. @@ -80,6 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures ViewContext viewContext, string linkText, string pageName, + string pageHandler, string protocol, string hostname, string fragment, @@ -161,7 +163,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures /// will process the request. /// /// A instance for the current scope. - /// The name of the action method. + /// The name of the page. + /// The page handler to generate a form for. /// /// An that contains the parameters for a route. The parameters are retrieved through /// reflection by examining the properties of the . This is typically @@ -180,6 +183,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures TagBuilder GeneratePageForm( ViewContext viewContext, string pageName, + string pageHandler, object routeValues, string fragment, string method, diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ControllerBaseTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ControllerBaseTest.cs index 1244681fb5..0a7de27d3c 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ControllerBaseTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ControllerBaseTest.cs @@ -1014,7 +1014,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test var pageName = "CustomRouteName"; // Act - var resultPermanent = pageModel.RedirectToPagePreserveMethod(pageName, routeValues); + var resultPermanent = pageModel.RedirectToPagePreserveMethod(pageName, routeValues: routeValues); // Assert Assert.IsType(resultPermanent); @@ -1035,7 +1035,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test var routeName = "CustomRouteName"; // Act - var resultPermanent = pageModel.RedirectToPagePermanentPreserveMethod(routeName, routeValues); + var resultPermanent = pageModel.RedirectToPagePermanentPreserveMethod(routeName, routeValues: routeValues); // Assert Assert.IsType(resultPermanent); diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/RedirectToPageResultTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/RedirectToPageResultTest.cs index f2e97a5a50..27c7dba121 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/RedirectToPageResultTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/RedirectToPageResultTest.cs @@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Mvc new ActionDescriptor()); var urlHelper = GetUrlHelper(actionContext, expectedUrl); - var result = new RedirectToPageResult("/MyPage", new { id = 10, test = "value" }, permanentRedirect) + var result = new RedirectToPageResult("/MyPage", null, new { id = 10, test = "value" }, permanentRedirect) { UrlHelper = urlHelper, }; @@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Mvc .Callback((UrlRouteContext c) => context = c) .Returns("some-value"); var values = new { test = "test-value" }; - var result = new RedirectToPageResult("/MyPage", values, true, "test-fragment") + var result = new RedirectToPageResult("/MyPage", "page-handler", values, true, "test-fragment") { UrlHelper = urlHelper.Object, Protocol = "ftp", @@ -121,6 +121,11 @@ namespace Microsoft.AspNetCore.Mvc { Assert.Equal("page", value.Key); Assert.Equal("/MyPage", value.Value); + }, + value => + { + Assert.Equal("handler", value.Key); + Assert.Equal("page-handler", value.Value); }); Assert.Equal("ftp", context.Protocol); Assert.Equal("test-fragment", context.Fragment); diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs index d03d8feab5..a1e337e607 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs @@ -1132,7 +1132,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing .Callback((UrlRouteContext context) => actual = context); // Act - urlHelper.Object.Page("/TestPage", new { id = 13 }, "https"); + urlHelper.Object.Page("/TestPage", pageHandler: null, values: new { id = 13 }, protocol: "https"); // Assert urlHelper.Verify(); @@ -1164,7 +1164,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing .Callback((UrlRouteContext context) => actual = context); // Act - urlHelper.Object.Page("/TestPage", new { id = 13 }, "https", "mytesthost"); + urlHelper.Object.Page("/TestPage", pageHandler: null, values: new { id = 13 }, protocol: "https", host: "mytesthost"); // Assert urlHelper.Verify(); @@ -1196,7 +1196,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing .Callback((UrlRouteContext context) => actual = context); // Act - urlHelper.Object.Page("/TestPage", new { id = 13 }, "https", "mytesthost", "#toc"); + urlHelper.Object.Page("/TestPage", "test-handler", new { id = 13 }, "https", "mytesthost", "#toc"); // Assert urlHelper.Verify(); @@ -1212,6 +1212,11 @@ namespace Microsoft.AspNetCore.Mvc.Routing { Assert.Equal("page", value.Key); Assert.Equal("/TestPage", value.Value); + }, + value => + { + Assert.Equal("handler", value.Key); + Assert.Equal("test-handler", value.Value); }); Assert.Equal("https", actual.Protocol); Assert.Equal("mytesthost", actual.Host); @@ -1241,7 +1246,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing // Act string page = null; - urlHelper.Object.Page(page, new { id = 13 }, "https", "mytesthost", "#toc"); + urlHelper.Object.Page(page, new { id = 13 }); // Assert urlHelper.Verify(); @@ -1258,9 +1263,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing Assert.Equal("page", value.Key); Assert.Equal("ambient-page", value.Value); }); - Assert.Equal("https", actual.Protocol); - Assert.Equal("mytesthost", actual.Host); - Assert.Equal("#toc", actual.Fragment); } [Fact] @@ -1287,7 +1289,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing // Act string page = null; - urlHelper.Object.Page(page, new { id = 13 }, "https", "mytesthost", "#toc"); + urlHelper.Object.Page(page, new { id = 13 }); // Assert urlHelper.Verify(); @@ -1335,7 +1337,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing // Act string page = null; - urlHelper.Object.Page(page, new { handler = "exact-handler" }, "https", "mytesthost", "#toc"); + urlHelper.Object.Page(page, "exact-handler", new { handler = "route-value-handler" }); // Assert urlHelper.Verify(); @@ -1354,6 +1356,49 @@ namespace Microsoft.AspNetCore.Mvc.Routing }); } + [Fact] + public void Page_UsesValueFromRouteValueIfPageHandlerIsNotExplicitySpecified() + { + // Arrange + UrlRouteContext actual = null; + var routeData = new RouteData + { + Values = + { + { "page", "ambient-page" }, + { "handler", "ambient-handler" }, + } + }; + var actionContext = new ActionContext + { + RouteData = routeData, + }; + + var urlHelper = CreateMockUrlHelper(actionContext); + urlHelper.Setup(h => h.RouteUrl(It.IsAny())) + .Callback((UrlRouteContext context) => actual = context); + + // Act + string page = null; + urlHelper.Object.Page(page, pageHandler: null, values: new { handler = "route-value-handler" }); + + // Assert + urlHelper.Verify(); + Assert.NotNull(actual); + Assert.Null(actual.RouteName); + Assert.Collection(Assert.IsType(actual.Values), + value => + { + Assert.Equal("handler", value.Key); + Assert.Equal("route-value-handler", value.Value); + }, + value => + { + Assert.Equal("page", value.Key); + Assert.Equal("ambient-page", value.Value); + }); + } + [Theory] [InlineData("Sibling", "/Dir1/Dir2/Sibling")] [InlineData("Dir3/Sibling", "/Dir1/Dir2/Dir3/Sibling")] diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesWithBasePathTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesWithBasePathTest.cs index 2458e23263..de8628a810 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesWithBasePathTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesWithBasePathTest.cs @@ -160,13 +160,15 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests public async Task FormTagHelper_WithPage_AllowsPostingToAnotherPage() { //Arrange - var expected = "
"; + var expected = +@"
+
"; // Act var response = await Client.GetStringAsync("/TagHelper/CrossPost"); // Assert - Assert.Contains(expected, response.Trim()); + Assert.Equal(expected, response.Trim()); } [Fact] @@ -176,7 +178,10 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests var expected = @"