[Fixes #1489] Support for named route in form tag helper
This commit is contained in:
parent
37e819ce85
commit
a80a333fea
|
|
@ -17,6 +17,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
private const string ActionAttributeName = "asp-action";
|
||||
private const string AntiForgeryAttributeName = "asp-anti-forgery";
|
||||
private const string ControllerAttributeName = "asp-controller";
|
||||
private const string RouteAttributeName = "asp-route";
|
||||
private const string RouteAttributePrefix = "asp-route-";
|
||||
private const string HtmlActionAttributeName = "action";
|
||||
|
||||
|
|
@ -47,6 +48,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
[HtmlAttributeName(AntiForgeryAttributeName)]
|
||||
public bool? AntiForgery { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the route.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Must be <c>null</c> if <see cref="Action"/> or <see cref="Controller"/> is non-<c>null</c>.
|
||||
/// </remarks>
|
||||
[HtmlAttributeName(RouteAttributeName)]
|
||||
public string Route { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Does nothing if user provides an <c>action</c> attribute and <see cref="AntiForgery"/> is <c>null</c> or
|
||||
|
|
@ -64,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
// If "action" is already set, it means the user is attempting to use a normal <form>.
|
||||
if (output.Attributes.ContainsKey(HtmlActionAttributeName))
|
||||
{
|
||||
if (Action != null || Controller != null || routePrefixedAttributes.Any())
|
||||
if (Action != null || Controller != null || Route != null || routePrefixedAttributes.Any())
|
||||
{
|
||||
// User also specified bound attributes we cannot use.
|
||||
throw new InvalidOperationException(
|
||||
|
|
@ -73,6 +83,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
HtmlActionAttributeName,
|
||||
ActionAttributeName,
|
||||
ControllerAttributeName,
|
||||
RouteAttributeName,
|
||||
RouteAttributePrefix));
|
||||
}
|
||||
|
||||
|
|
@ -82,13 +93,38 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
}
|
||||
else
|
||||
{
|
||||
TagBuilder tagBuilder;
|
||||
var routeValues = GetRouteValues(output, routePrefixedAttributes);
|
||||
var tagBuilder = Generator.GenerateForm(ViewContext,
|
||||
Action,
|
||||
Controller,
|
||||
routeValues,
|
||||
method: null,
|
||||
htmlAttributes: null);
|
||||
if (Route == null)
|
||||
{
|
||||
tagBuilder = Generator.GenerateForm(
|
||||
ViewContext,
|
||||
Action,
|
||||
Controller,
|
||||
routeValues,
|
||||
method: null,
|
||||
htmlAttributes: null);
|
||||
}
|
||||
else if (Action != null || Controller != null)
|
||||
{
|
||||
// Route and Action or Controller were specified. Can't determine the action attribute.
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatFormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified(
|
||||
"<form>",
|
||||
RouteAttributeName,
|
||||
ActionAttributeName,
|
||||
ControllerAttributeName,
|
||||
HtmlActionAttributeName));
|
||||
}
|
||||
else
|
||||
{
|
||||
tagBuilder = Generator.GenerateRouteForm(
|
||||
ViewContext,
|
||||
Route,
|
||||
routeValues,
|
||||
method: null,
|
||||
htmlAttributes: null);
|
||||
}
|
||||
|
||||
if (tagBuilder != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{4}' or an '{2}' or '{3}' attribute.
|
||||
/// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{5}' or an '{2}' or '{3}' or '{4}' attribute.
|
||||
/// </summary>
|
||||
internal static string FormTagHelper_CannotOverrideAction
|
||||
{
|
||||
|
|
@ -51,11 +51,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{4}' or an '{2}' or '{3}' attribute.
|
||||
/// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{5}' or an '{2}' or '{3}' or '{4}' attribute.
|
||||
/// </summary>
|
||||
internal static string FormatFormTagHelper_CannotOverrideAction(object p0, object p1, object p2, object p3, object p4)
|
||||
internal static string FormatFormTagHelper_CannotOverrideAction(object p0, object p1, object p2, object p3, object p4, object p5)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotOverrideAction"), p0, p1, p2, p3, p4);
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotOverrideAction"), p0, p1, p2, p3, p4, p5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -138,6 +138,22 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperOutput_AttributeDoesNotExist"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.
|
||||
/// </summary>
|
||||
internal static string FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified
|
||||
{
|
||||
get { return GetString("FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.
|
||||
/// </summary>
|
||||
internal static string FormatFormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified"), p0, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@
|
|||
<value>Cannot override the '{8}' attribute for {0}. An {0} with a specified '{8}' must not have attributes starting with '{7}' or an '{1}', '{2}', '{3}', '{4}', '{5}', or '{6}' attribute.</value>
|
||||
</data>
|
||||
<data name="FormTagHelper_CannotOverrideAction" xml:space="preserve">
|
||||
<value>Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{4}' or an '{2}' or '{3}' attribute.</value>
|
||||
<value>Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{5}' or an '{2}' or '{3}' or '{4}' attribute.</value>
|
||||
</data>
|
||||
<data name="InputTagHelper_InvalidExpressionResult" xml:space="preserve">
|
||||
<value>Unexpected '{1}' expression result type '{2}' for {0}. '{1}' must be of type '{3}' if '{4}' is '{5}'.</value>
|
||||
|
|
@ -141,4 +141,7 @@
|
|||
<data name="TagHelperOutput_AttributeDoesNotExist" xml:space="preserve">
|
||||
<value>The attribute '{0}' does not exist in the {1}.</value>
|
||||
</data>
|
||||
<data name="FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified" xml:space="preserve">
|
||||
<value>Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -252,11 +252,65 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
Assert.Equal("form", output.TagName);
|
||||
Assert.False(output.SelfClosing);
|
||||
Assert.Empty(output.Attributes);
|
||||
Assert.Empty(output.PreElement.GetContent());
|
||||
Assert.Empty(output.PreContent.GetContent());
|
||||
Assert.True(output.Content.IsEmpty);
|
||||
Assert.Empty(output.PostContent.GetContent());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_CallsIntoGenerateRouteFormWithExpectedParameters()
|
||||
{
|
||||
// Arrange
|
||||
var viewContext = CreateViewContext();
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: new Dictionary<string, object>(),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test",
|
||||
getChildContentAsync: () =>
|
||||
{
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
tagHelperContent.SetContent("Something");
|
||||
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||
});
|
||||
var output = new TagHelperOutput(
|
||||
"form",
|
||||
attributes: new Dictionary<string, object>
|
||||
{
|
||||
{ "asp-route-foo", "bar" }
|
||||
});
|
||||
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
||||
generator
|
||||
.Setup(mock => mock.GenerateRouteForm(
|
||||
viewContext,
|
||||
"Default",
|
||||
It.Is<Dictionary<string, object>>(m => string.Equals(m["foo"], "bar")),
|
||||
null,
|
||||
null))
|
||||
.Returns(new TagBuilder("form", new CommonTestEncoder()))
|
||||
.Verifiable();
|
||||
var formTagHelper = new FormTagHelper
|
||||
{
|
||||
AntiForgery = false,
|
||||
Route = "Default",
|
||||
Generator = generator.Object,
|
||||
ViewContext = viewContext,
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
await formTagHelper.ProcessAsync(context, output);
|
||||
generator.Verify();
|
||||
|
||||
Assert.Equal("form", output.TagName);
|
||||
Assert.False(output.SelfClosing);
|
||||
Assert.Empty(output.Attributes);
|
||||
Assert.Empty(output.PreElement.GetContent());
|
||||
Assert.Empty(output.PreContent.GetContent());
|
||||
Assert.True(output.Content.IsEmpty);
|
||||
Assert.Empty(output.PostContent.GetContent());
|
||||
Assert.Empty(output.PostElement.GetContent());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, "<input />")]
|
||||
[InlineData(false, "")]
|
||||
|
|
@ -333,7 +387,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
|
||||
var expectedErrorMessage = "Cannot override the 'action' attribute for <form>. A <form> with a specified " +
|
||||
"'action' must not have attributes starting with 'asp-route-' or an " +
|
||||
"'asp-action' or 'asp-controller' attribute.";
|
||||
"'asp-action' or 'asp-controller' or 'asp-route' attribute.";
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
|
|
@ -342,6 +396,30 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Action")]
|
||||
[InlineData("Controller")]
|
||||
public async Task ProcessAsync_ThrowsIfRouteAndActionOrControllerProvided(string propertyName)
|
||||
{
|
||||
// Arrange
|
||||
var formTagHelper = new FormTagHelper
|
||||
{
|
||||
Route = "Default",
|
||||
};
|
||||
typeof(FormTagHelper).GetProperty(propertyName).SetValue(formTagHelper, "Home");
|
||||
var output = new TagHelperOutput(
|
||||
"form",
|
||||
attributes: new Dictionary<string, object>());
|
||||
var expectedErrorMessage = "Cannot determine an 'action' attribute for <form>. A <form> with a specified " +
|
||||
"'asp-route' must not have an 'asp-action' or 'asp-controller' attribute.";
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => formTagHelper.ProcessAsync(context: null, output: output));
|
||||
|
||||
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||
}
|
||||
|
||||
private static ViewContext CreateViewContext()
|
||||
{
|
||||
var actionContext = new ActionContext(
|
||||
|
|
|
|||
Loading…
Reference in New Issue