From 8bece90c8309944f2ba0e247c829ea2c4fae7db6 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Wed, 26 Apr 2017 16:18:57 -0700 Subject: [PATCH] Fix asp-page-handler to generate form tags correctly. - Prior to this change using asp-page-handler on its own did not create correct `
` elements. There were multiple issues, one as that the `FormTagHelper` would purposefully drop into a no-op code path. Second is the `DefaultHtmlGenerator` didn't call through to the `UrlHelper` correctly. - Added functional test cases to validate asp-page-handler can live on its own on a form tag. This also included adding a variant where method="post". - Added a `FormTagHelper` unit test to validate the `PageHandler` property is consumed properly. #6208 --- .../FormTagHelper.cs | 24 ++++------ .../ViewFeatures/DefaultHtmlGenerator.cs | 30 +----------- .../RazorPagesWithBasePathTest.cs | 20 ++++++++ .../FormTagHelperTest.cs | 47 +++++++++++++++++++ .../Pages/TagHelper/PostWithHandler.cshtml | 4 +- 5 files changed, 82 insertions(+), 43 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs index 33d398055b..ad10121751 100644 --- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs +++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs @@ -159,18 +159,19 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers } var antiforgeryDefault = true; + var routeableParametersProvided = Action != null || + Controller != null || + Area != null || + Page != null || + PageHandler != null || + Fragment != null || + Route != null || + (_routeValues != null && _routeValues.Count > 0); // If "action" is already set, it means the user is attempting to use a normal . if (output.Attributes.TryGetAttribute(HtmlActionAttributeName, out var actionAttribute)) { - if (Action != null || - Controller != null || - Area != null || - Page != null || - PageHandler != null || - Fragment != null || - Route != null || - (_routeValues != null && _routeValues.Count > 0)) + if (routeableParametersProvided) { // User also specified bound attributes we cannot use. throw new InvalidOperationException( @@ -254,13 +255,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers } TagBuilder tagBuilder = null; - if (Action == null && - Controller == null && - Route == null && + if (!routeableParametersProvided && _routeValues == null && - Fragment == null && - Area == null && - Page == null && // Antiforgery will sometime be set globally via TagHelper Initializers, verify it was provided in the cshtml. !context.AllAttributes.ContainsName(AntiforgeryAttributeName)) { diff --git a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs index 94ac30803a..d06ded41b6 100644 --- a/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs +++ b/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/DefaultHtmlGenerator.cs @@ -323,34 +323,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures throw new ArgumentNullException(nameof(viewContext)); } - var defaultMethod = false; - if (string.IsNullOrEmpty(method)) - { - defaultMethod = true; - } - else if (string.Equals(method, "post", StringComparison.OrdinalIgnoreCase)) - { - defaultMethod = true; - } - - string action; - if (pageName == null && routeValues == null && defaultMethod) - { - // Submit to the original URL in the special case that user called the BeginForm() overload without - // parameters (except for the htmlAttributes parameter). Also reachable in the even-more-unusual case - // that user called another BeginForm() overload with default argument values. - var request = viewContext.HttpContext.Request; - action = request.PathBase + request.Path + request.QueryString; - if (fragment != null) - { - action += "#" + fragment; - } - } - else - { - var urlHelper = _urlHelperFactory.GetUrlHelper(viewContext); - action = urlHelper.Page(pageName, pageHandler, routeValues, protocol: null, host: null, fragment: fragment); - } + var urlHelper = _urlHelperFactory.GetUrlHelper(viewContext); + var action = urlHelper.Page(pageName, pageHandler, routeValues, protocol: null, host: null, fragment: fragment); return GenerateFormCore(viewContext, action, method, htmlAttributes); } diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesWithBasePathTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesWithBasePathTest.cs index de8628a810..ea6311fa8c 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesWithBasePathTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesWithBasePathTest.cs @@ -156,6 +156,26 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Contains(expected, response.Trim()); } + [Fact] + public async Task FormTagHelper_WithPageHandler_AllowsPostingToSelf() + { + //Arrange + var expected = +@"
+
+
"; + + // Act + var response = await Client.GetStringAsync("/TagHelper/PostWithHandler"); + + // Assert + var responseContent = response.Trim(); + var forgeryToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(responseContent, "/TagHelper/PostWithHandler"); + var expectedContent = string.Format(expected, forgeryToken); + + Assert.Equal(expectedContent, responseContent, ignoreLineEndingDifferences: true); + } + [Fact] public async Task FormTagHelper_WithPage_AllowsPostingToAnotherPage() { diff --git a/test/Microsoft.AspNetCore.Mvc.TagHelpers.Test/FormTagHelperTest.cs b/test/Microsoft.AspNetCore.Mvc.TagHelpers.Test/FormTagHelperTest.cs index 9970667ae4..88ca0ebc5a 100644 --- a/test/Microsoft.AspNetCore.Mvc.TagHelpers.Test/FormTagHelperTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.TagHelpers.Test/FormTagHelperTest.cs @@ -25,6 +25,53 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers { public class FormTagHelperTest { + [Fact] + public async Task ProcessAsync_InvokesGeneratePageForm_WithOnlyPageHandler() + { + // Arrange + var viewContext = CreateViewContext(); + var context = new TagHelperContext( + tagName: "form", + allAttributes: new TagHelperAttributeList() + { + { "asp-handler", "page-handler" }, + { "method", "get" } + }, + items: new Dictionary(), + uniqueId: "test"); + var output = new TagHelperOutput( + "form", + attributes: new TagHelperAttributeList(), + getChildContentAsync: (useCachedResult, encoder) => + { + var tagHelperContent = new DefaultTagHelperContent(); + tagHelperContent.SetContent("Something"); + return Task.FromResult(tagHelperContent); + }); + var generator = new Mock(MockBehavior.Strict); + generator + .Setup(mock => mock.GeneratePageForm( + viewContext, + null, + "page-handler", + null, + null, + null, + null)) + .Returns(new TagBuilder("form")) + .Verifiable(); + var formTagHelper = new FormTagHelper(generator.Object) + { + ViewContext = viewContext, + PageHandler = "page-handler", + Method = "get" + }; + + // Act & Assert + await formTagHelper.ProcessAsync(context, output); + generator.Verify(); + } + [Fact] public async Task ProcessAsync_ActionAndControllerGenerateAntiforgery() { diff --git a/test/WebSites/RazorPagesWebSite/Pages/TagHelper/PostWithHandler.cshtml b/test/WebSites/RazorPagesWebSite/Pages/TagHelper/PostWithHandler.cshtml index 2b5067e71a..39ef8f5c4a 100644 --- a/test/WebSites/RazorPagesWebSite/Pages/TagHelper/PostWithHandler.cshtml +++ b/test/WebSites/RazorPagesWebSite/Pages/TagHelper/PostWithHandler.cshtml @@ -1,6 +1,6 @@ @page "{handler?}/{id?}" -@function +@functions { public IActionResult OnPostEdit(int id) { @@ -13,4 +13,6 @@ } } +
+