Fix asp-page-handler to generate form tags correctly.
- Prior to this change using asp-page-handler on its own did not create correct `<form>` 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
This commit is contained in:
parent
b1b3a816cc
commit
8bece90c83
|
|
@ -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 <form>.
|
||||
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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,6 +156,26 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Contains(expected, response.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FormTagHelper_WithPageHandler_AllowsPostingToSelf()
|
||||
{
|
||||
//Arrange
|
||||
var expected =
|
||||
@"<form action=""/TagHelper/PostWithHandler/Edit"" method=""post""><input name=""__RequestVerificationToken"" type=""hidden"" value=""{0}"" /></form>
|
||||
<form method=""post"" action=""/TagHelper/PostWithHandler/Edit""><input name=""__RequestVerificationToken"" type=""hidden"" value=""{0}"" /></form>
|
||||
<form method=""post"" action=""/TagHelper/PostWithHandler/Edit/10""></form>";
|
||||
|
||||
// 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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
"form",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
var generator = new Mock<IHtmlGenerator>(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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@page "{handler?}/{id?}"
|
||||
|
||||
@function
|
||||
@functions
|
||||
{
|
||||
public IActionResult OnPostEdit(int id)
|
||||
{
|
||||
|
|
@ -13,4 +13,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
<form asp-page-handler="Edit"></form>
|
||||
<form method="post" asp-page-handler="Edit"></form>
|
||||
<form method="post" asp-page-handler="Edit" asp-route-id="10" asp-antiforgery="false"></form>
|
||||
|
|
|
|||
Loading…
Reference in New Issue