parent
f568d3c2bc
commit
ca017eced2
|
|
@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
[HtmlTargetElement("a", Attributes = ActionAttributeName)]
|
[HtmlTargetElement("a", Attributes = ActionAttributeName)]
|
||||||
[HtmlTargetElement("a", Attributes = ControllerAttributeName)]
|
[HtmlTargetElement("a", Attributes = ControllerAttributeName)]
|
||||||
[HtmlTargetElement("a", Attributes = AreaAttributeName)]
|
[HtmlTargetElement("a", Attributes = AreaAttributeName)]
|
||||||
|
[HtmlTargetElement("a", Attributes = PageAttributeName)]
|
||||||
[HtmlTargetElement("a", Attributes = FragmentAttributeName)]
|
[HtmlTargetElement("a", Attributes = FragmentAttributeName)]
|
||||||
[HtmlTargetElement("a", Attributes = HostAttributeName)]
|
[HtmlTargetElement("a", Attributes = HostAttributeName)]
|
||||||
[HtmlTargetElement("a", Attributes = ProtocolAttributeName)]
|
[HtmlTargetElement("a", Attributes = ProtocolAttributeName)]
|
||||||
|
|
@ -27,6 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
private const string ActionAttributeName = "asp-action";
|
private const string ActionAttributeName = "asp-action";
|
||||||
private const string ControllerAttributeName = "asp-controller";
|
private const string ControllerAttributeName = "asp-controller";
|
||||||
private const string AreaAttributeName = "asp-area";
|
private const string AreaAttributeName = "asp-area";
|
||||||
|
private const string PageAttributeName = "asp-page";
|
||||||
private const string FragmentAttributeName = "asp-fragment";
|
private const string FragmentAttributeName = "asp-fragment";
|
||||||
private const string HostAttributeName = "asp-host";
|
private const string HostAttributeName = "asp-host";
|
||||||
private const string ProtocolAttributeName = "asp-protocol";
|
private const string ProtocolAttributeName = "asp-protocol";
|
||||||
|
|
@ -59,24 +61,40 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the action method.
|
/// The name of the action method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Must be <c>null</c> if <see cref="Route"/> is non-<c>null</c>.</remarks>
|
/// <remarks>
|
||||||
|
/// Must be <c>null</c> if <see cref="Route"/> or <see cref="Page"/> is non-<c>null</c>.
|
||||||
|
/// </remarks>
|
||||||
[HtmlAttributeName(ActionAttributeName)]
|
[HtmlAttributeName(ActionAttributeName)]
|
||||||
public string Action { get; set; }
|
public string Action { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the controller.
|
/// The name of the controller.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Must be <c>null</c> if <see cref="Route"/> is non-<c>null</c>.</remarks>
|
/// <remarks>
|
||||||
|
/// Must be <c>null</c> if <see cref="Route"/> or <see cref="Page"/> is non-<c>null</c>.
|
||||||
|
/// </remarks>
|
||||||
[HtmlAttributeName(ControllerAttributeName)]
|
[HtmlAttributeName(ControllerAttributeName)]
|
||||||
public string Controller { get; set; }
|
public string Controller { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the area.
|
/// The name of the area.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Must be <c>null</c> if <see cref="Route"/> is non-<c>null</c>.</remarks>
|
/// <remarks>
|
||||||
|
/// Must be <c>null</c> if <see cref="Route"/> or <see cref="Page"/> is non-<c>null</c>.
|
||||||
|
/// </remarks>
|
||||||
[HtmlAttributeName(AreaAttributeName)]
|
[HtmlAttributeName(AreaAttributeName)]
|
||||||
public string Area { get; set; }
|
public string Area { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the page.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Must be <c>null</c> if <see cref="Route"/> or <see cref="Action"/>, <see cref="Controller"/>
|
||||||
|
/// or <see cref="Area"/> is non-<c>null</c>.
|
||||||
|
/// </remarks>
|
||||||
|
[HtmlAttributeName(PageAttributeName)]
|
||||||
|
public string Page { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The protocol for the URL, such as "http" or "https".
|
/// The protocol for the URL, such as "http" or "https".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -99,7 +117,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
/// Name of the route.
|
/// Name of the route.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Must be <c>null</c> if <see cref="Action"/> or <see cref="Controller"/> is non-<c>null</c>.
|
/// Must be <c>null</c> if one of <see cref="Action"/>, <see cref="Controller"/>, <see cref="Area"/>
|
||||||
|
/// or <see cref="Page"/> is non-<c>null</c>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[HtmlAttributeName(RouteAttributeName)]
|
[HtmlAttributeName(RouteAttributeName)]
|
||||||
public string Route { get; set; }
|
public string Route { get; set; }
|
||||||
|
|
@ -134,12 +153,6 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
/// <remarks>Does nothing if user provides an <c>href</c> attribute.</remarks>
|
/// <remarks>Does nothing if user provides an <c>href</c> attribute.</remarks>
|
||||||
/// <exception cref="InvalidOperationException">
|
|
||||||
/// Thrown if <c>href</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>,
|
|
||||||
/// <see cref="Fragment"/>, <see cref="Host"/>, <see cref="Protocol"/>, or <see cref="Route"/> are
|
|
||||||
/// non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes. Also thrown if <see cref="Route"/>
|
|
||||||
/// and one or both of <see cref="Action"/> and <see cref="Controller"/> are non-<c>null</c>.
|
|
||||||
/// </exception>
|
|
||||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
|
|
@ -158,6 +171,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
if (Action != null ||
|
if (Action != null ||
|
||||||
Controller != null ||
|
Controller != null ||
|
||||||
Area != null ||
|
Area != null ||
|
||||||
|
Page != null ||
|
||||||
Route != null ||
|
Route != null ||
|
||||||
Protocol != null ||
|
Protocol != null ||
|
||||||
Host != null ||
|
Host != null ||
|
||||||
|
|
@ -167,7 +181,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
// User specified an href and one of the bound attributes; can't determine the href attribute.
|
// User specified an href and one of the bound attributes; can't determine the href attribute.
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
Resources.FormatAnchorTagHelper_CannotOverrideHref(
|
Resources.FormatAnchorTagHelper_CannotOverrideHref(
|
||||||
|
Href,
|
||||||
"<a>",
|
"<a>",
|
||||||
|
RouteValuesPrefix,
|
||||||
ActionAttributeName,
|
ActionAttributeName,
|
||||||
ControllerAttributeName,
|
ControllerAttributeName,
|
||||||
AreaAttributeName,
|
AreaAttributeName,
|
||||||
|
|
@ -175,72 +191,84 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
ProtocolAttributeName,
|
ProtocolAttributeName,
|
||||||
HostAttributeName,
|
HostAttributeName,
|
||||||
FragmentAttributeName,
|
FragmentAttributeName,
|
||||||
RouteValuesPrefix,
|
PageAttributeName));
|
||||||
Href));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var routeLink = Route != null;
|
||||||
|
var actionLink = Controller != null || Action != null;
|
||||||
|
var pageLink = Page != null;
|
||||||
|
|
||||||
|
if ((routeLink && actionLink) || (routeLink && pageLink) || (actionLink && pageLink))
|
||||||
|
{
|
||||||
|
var message = string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
Resources.FormatCannotDetermineAttributeFor(Href, "<a>"),
|
||||||
|
RouteAttributeName,
|
||||||
|
ControllerAttributeName + ", " + ActionAttributeName,
|
||||||
|
PageAttributeName);
|
||||||
|
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteValueDictionary routeValues = null;
|
||||||
|
if (_routeValues != null && _routeValues.Count > 0)
|
||||||
|
{
|
||||||
|
routeValues = new RouteValueDictionary(_routeValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Area != null)
|
||||||
|
{
|
||||||
|
// Unconditionally replace any value from asp-route-area.
|
||||||
|
if (routeValues == null)
|
||||||
|
{
|
||||||
|
routeValues = new RouteValueDictionary();
|
||||||
|
}
|
||||||
|
routeValues["area"] = Area;
|
||||||
|
}
|
||||||
|
|
||||||
|
TagBuilder tagBuilder;
|
||||||
|
if (pageLink)
|
||||||
|
{
|
||||||
|
tagBuilder = Generator.GeneratePageLink(
|
||||||
|
ViewContext,
|
||||||
|
linkText: string.Empty,
|
||||||
|
pageName: Page,
|
||||||
|
protocol: Protocol,
|
||||||
|
hostname: Host,
|
||||||
|
fragment: Fragment,
|
||||||
|
routeValues: routeValues,
|
||||||
|
htmlAttributes: null);
|
||||||
|
}
|
||||||
|
else if (routeLink)
|
||||||
|
{
|
||||||
|
tagBuilder = Generator.GenerateRouteLink(
|
||||||
|
ViewContext,
|
||||||
|
linkText: string.Empty,
|
||||||
|
routeName: Route,
|
||||||
|
protocol: Protocol,
|
||||||
|
hostName: Host,
|
||||||
|
fragment: Fragment,
|
||||||
|
routeValues: routeValues,
|
||||||
|
htmlAttributes: null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RouteValueDictionary routeValues = null;
|
tagBuilder = Generator.GenerateActionLink(
|
||||||
if (_routeValues != null && _routeValues.Count > 0)
|
ViewContext,
|
||||||
{
|
linkText: string.Empty,
|
||||||
routeValues = new RouteValueDictionary(_routeValues);
|
actionName: Action,
|
||||||
}
|
controllerName: Controller,
|
||||||
|
protocol: Protocol,
|
||||||
if (Area != null)
|
hostname: Host,
|
||||||
{
|
fragment: Fragment,
|
||||||
if (routeValues == null)
|
routeValues: routeValues,
|
||||||
{
|
htmlAttributes: null);
|
||||||
routeValues = new RouteValueDictionary();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unconditionally replace any value from asp-route-area.
|
|
||||||
routeValues["area"] = Area;
|
|
||||||
}
|
|
||||||
|
|
||||||
TagBuilder tagBuilder;
|
|
||||||
if (Route == null)
|
|
||||||
{
|
|
||||||
tagBuilder = Generator.GenerateActionLink(
|
|
||||||
ViewContext,
|
|
||||||
linkText: string.Empty,
|
|
||||||
actionName: Action,
|
|
||||||
controllerName: Controller,
|
|
||||||
protocol: Protocol,
|
|
||||||
hostname: Host,
|
|
||||||
fragment: Fragment,
|
|
||||||
routeValues: routeValues,
|
|
||||||
htmlAttributes: null);
|
|
||||||
}
|
|
||||||
else if (Action != null || Controller != null)
|
|
||||||
{
|
|
||||||
// Route and Action or Controller were specified. Can't determine the href attribute.
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
Resources.FormatAnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified(
|
|
||||||
"<a>",
|
|
||||||
RouteAttributeName,
|
|
||||||
ActionAttributeName,
|
|
||||||
ControllerAttributeName,
|
|
||||||
Href));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tagBuilder = Generator.GenerateRouteLink(
|
|
||||||
ViewContext,
|
|
||||||
linkText: string.Empty,
|
|
||||||
routeName: Route,
|
|
||||||
protocol: Protocol,
|
|
||||||
hostName: Host,
|
|
||||||
fragment: Fragment,
|
|
||||||
routeValues: routeValues,
|
|
||||||
htmlAttributes: null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tagBuilder != null)
|
|
||||||
{
|
|
||||||
output.MergeAttributes(tagBuilder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output.MergeAttributes(tagBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
[HtmlTargetElement("button", Attributes = ActionAttributeName)]
|
[HtmlTargetElement("button", Attributes = ActionAttributeName)]
|
||||||
[HtmlTargetElement("button", Attributes = ControllerAttributeName)]
|
[HtmlTargetElement("button", Attributes = ControllerAttributeName)]
|
||||||
[HtmlTargetElement("button", Attributes = AreaAttributeName)]
|
[HtmlTargetElement("button", Attributes = AreaAttributeName)]
|
||||||
|
[HtmlTargetElement("button", Attributes = PageAttributeName)]
|
||||||
[HtmlTargetElement("button", Attributes = FragmentAttributeName)]
|
[HtmlTargetElement("button", Attributes = FragmentAttributeName)]
|
||||||
[HtmlTargetElement("button", Attributes = RouteAttributeName)]
|
[HtmlTargetElement("button", Attributes = RouteAttributeName)]
|
||||||
[HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)]
|
[HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)]
|
||||||
|
|
@ -25,6 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
[HtmlTargetElement("input", Attributes = ImageActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = ImageActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
[HtmlTargetElement("input", Attributes = ImageControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = ImageControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
[HtmlTargetElement("input", Attributes = ImageAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = ImageAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
|
[HtmlTargetElement("input", Attributes = ImagePageAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
[HtmlTargetElement("input", Attributes = ImageFragmentAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = ImageFragmentAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
[HtmlTargetElement("input", Attributes = ImageRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = ImageRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
[HtmlTargetElement("input", Attributes = ImageRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = ImageRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
|
|
@ -32,6 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
[HtmlTargetElement("input", Attributes = SubmitActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = SubmitActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
[HtmlTargetElement("input", Attributes = SubmitControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = SubmitControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
[HtmlTargetElement("input", Attributes = SubmitAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = SubmitAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
|
[HtmlTargetElement("input", Attributes = SubmitPageAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
[HtmlTargetElement("input", Attributes = SubmitFragmentAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = SubmitFragmentAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
[HtmlTargetElement("input", Attributes = SubmitRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = SubmitRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
[HtmlTargetElement("input", Attributes = SubmitRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)]
|
[HtmlTargetElement("input", Attributes = SubmitRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||||
|
|
@ -41,6 +44,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
private const string ActionAttributeName = "asp-action";
|
private const string ActionAttributeName = "asp-action";
|
||||||
private const string AreaAttributeName = "asp-area";
|
private const string AreaAttributeName = "asp-area";
|
||||||
private const string ControllerAttributeName = "asp-controller";
|
private const string ControllerAttributeName = "asp-controller";
|
||||||
|
private const string PageAttributeName = "asp-page";
|
||||||
private const string FragmentAttributeName = "asp-fragment";
|
private const string FragmentAttributeName = "asp-fragment";
|
||||||
private const string RouteAttributeName = "asp-route";
|
private const string RouteAttributeName = "asp-route";
|
||||||
private const string RouteValuesDictionaryName = "asp-all-route-data";
|
private const string RouteValuesDictionaryName = "asp-all-route-data";
|
||||||
|
|
@ -50,6 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
private const string ImageTypeSelector = "[type=image], ";
|
private const string ImageTypeSelector = "[type=image], ";
|
||||||
private const string ImageActionAttributeSelector = ImageTypeSelector + ActionAttributeName;
|
private const string ImageActionAttributeSelector = ImageTypeSelector + ActionAttributeName;
|
||||||
private const string ImageAreaAttributeSelector = ImageTypeSelector + AreaAttributeName;
|
private const string ImageAreaAttributeSelector = ImageTypeSelector + AreaAttributeName;
|
||||||
|
private const string ImagePageAttributeSelector = ImageTypeSelector + PageAttributeName;
|
||||||
private const string ImageFragmentAttributeSelector = ImageTypeSelector + FragmentAttributeName;
|
private const string ImageFragmentAttributeSelector = ImageTypeSelector + FragmentAttributeName;
|
||||||
private const string ImageControllerAttributeSelector = ImageTypeSelector + ControllerAttributeName;
|
private const string ImageControllerAttributeSelector = ImageTypeSelector + ControllerAttributeName;
|
||||||
private const string ImageRouteAttributeSelector = ImageTypeSelector + RouteAttributeName;
|
private const string ImageRouteAttributeSelector = ImageTypeSelector + RouteAttributeName;
|
||||||
|
|
@ -59,6 +64,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
private const string SubmitTypeSelector = "[type=submit], ";
|
private const string SubmitTypeSelector = "[type=submit], ";
|
||||||
private const string SubmitActionAttributeSelector = SubmitTypeSelector + ActionAttributeName;
|
private const string SubmitActionAttributeSelector = SubmitTypeSelector + ActionAttributeName;
|
||||||
private const string SubmitAreaAttributeSelector = SubmitTypeSelector + AreaAttributeName;
|
private const string SubmitAreaAttributeSelector = SubmitTypeSelector + AreaAttributeName;
|
||||||
|
private const string SubmitPageAttributeSelector = SubmitTypeSelector + PageAttributeName;
|
||||||
private const string SubmitFragmentAttributeSelector = SubmitTypeSelector + FragmentAttributeName;
|
private const string SubmitFragmentAttributeSelector = SubmitTypeSelector + FragmentAttributeName;
|
||||||
private const string SubmitControllerAttributeSelector = SubmitTypeSelector + ControllerAttributeName;
|
private const string SubmitControllerAttributeSelector = SubmitTypeSelector + ControllerAttributeName;
|
||||||
private const string SubmitRouteAttributeSelector = SubmitTypeSelector + RouteAttributeName;
|
private const string SubmitRouteAttributeSelector = SubmitTypeSelector + RouteAttributeName;
|
||||||
|
|
@ -106,6 +112,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
[HtmlAttributeName(AreaAttributeName)]
|
[HtmlAttributeName(AreaAttributeName)]
|
||||||
public string Area { get; set; }
|
public string Area { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the page.
|
||||||
|
/// </summary>
|
||||||
|
[HtmlAttributeName(PageAttributeName)]
|
||||||
|
public string Page { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the URL fragment.
|
/// Gets or sets the URL fragment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -185,51 +197,59 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
RouteValuesPrefix,
|
RouteValuesPrefix,
|
||||||
FormAction));
|
FormAction));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var routeLink = Route != null;
|
||||||
|
var actionLink = Controller != null || Action != null;
|
||||||
|
var pageLink = Page != null;
|
||||||
|
|
||||||
|
if ((routeLink && actionLink) || (routeLink && pageLink) || (actionLink && pageLink))
|
||||||
|
{
|
||||||
|
var message = string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
Resources.FormatCannotDetermineAttributeFor(FormAction, '<' + output.TagName + '>'),
|
||||||
|
RouteAttributeName,
|
||||||
|
ControllerAttributeName + ", " + ActionAttributeName,
|
||||||
|
PageAttributeName);
|
||||||
|
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteValueDictionary routeValues = null;
|
||||||
|
if (_routeValues != null && _routeValues.Count > 0)
|
||||||
|
{
|
||||||
|
routeValues = new RouteValueDictionary(_routeValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Area != null)
|
||||||
|
{
|
||||||
|
if (routeValues == null)
|
||||||
|
{
|
||||||
|
routeValues = new RouteValueDictionary();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unconditionally replace any value from asp-route-area.
|
||||||
|
routeValues["area"] = Area;
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
|
||||||
|
string url;
|
||||||
|
if (pageLink)
|
||||||
|
{
|
||||||
|
url = urlHelper.Page(Page, routeValues, protocol: null, host: null, fragment: Fragment);
|
||||||
|
}
|
||||||
|
else if (routeLink)
|
||||||
|
{
|
||||||
|
url = urlHelper.RouteUrl(Route, routeValues, protocol: null, host: null, fragment: Fragment);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RouteValueDictionary routeValues = null;
|
url = urlHelper.Action(Action, Controller, routeValues, protocol: null, host: null, fragment: Fragment);
|
||||||
if (_routeValues != null && _routeValues.Count > 0)
|
|
||||||
{
|
|
||||||
routeValues = new RouteValueDictionary(_routeValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Area != null)
|
|
||||||
{
|
|
||||||
if (routeValues == null)
|
|
||||||
{
|
|
||||||
routeValues = new RouteValueDictionary();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unconditionally replace any value from asp-route-area.
|
|
||||||
routeValues["area"] = Area;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Route == null)
|
|
||||||
{
|
|
||||||
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
|
|
||||||
var url = urlHelper.Action(Action, Controller, routeValues, protocol: null, host: null, fragment: Fragment);
|
|
||||||
output.Attributes.SetAttribute(FormAction, url);
|
|
||||||
}
|
|
||||||
else if (Action != null || Controller != null)
|
|
||||||
{
|
|
||||||
// Route and Action or Controller were specified. Can't determine the formaction attribute.
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
Resources.FormatFormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(
|
|
||||||
output.TagName,
|
|
||||||
RouteAttributeName,
|
|
||||||
ActionAttributeName,
|
|
||||||
ControllerAttributeName,
|
|
||||||
FormAction,
|
|
||||||
FragmentAttributeName));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
|
|
||||||
var url = urlHelper.RouteUrl(Route, routeValues, protocol: null, host: null, fragment: Fragment);
|
|
||||||
output.Attributes.SetAttribute(FormAction, url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output.Attributes.SetAttribute(FormAction, url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ using System.ComponentModel;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
{
|
{
|
||||||
|
|
@ -16,6 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
[HtmlTargetElement("form", Attributes = ActionAttributeName)]
|
[HtmlTargetElement("form", Attributes = ActionAttributeName)]
|
||||||
[HtmlTargetElement("form", Attributes = AntiforgeryAttributeName)]
|
[HtmlTargetElement("form", Attributes = AntiforgeryAttributeName)]
|
||||||
[HtmlTargetElement("form", Attributes = AreaAttributeName)]
|
[HtmlTargetElement("form", Attributes = AreaAttributeName)]
|
||||||
|
[HtmlTargetElement("form", Attributes = PageAttributeName)]
|
||||||
[HtmlTargetElement("form", Attributes = FragmentAttributeName)]
|
[HtmlTargetElement("form", Attributes = FragmentAttributeName)]
|
||||||
[HtmlTargetElement("form", Attributes = ControllerAttributeName)]
|
[HtmlTargetElement("form", Attributes = ControllerAttributeName)]
|
||||||
[HtmlTargetElement("form", Attributes = RouteAttributeName)]
|
[HtmlTargetElement("form", Attributes = RouteAttributeName)]
|
||||||
|
|
@ -26,6 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
private const string ActionAttributeName = "asp-action";
|
private const string ActionAttributeName = "asp-action";
|
||||||
private const string AntiforgeryAttributeName = "asp-antiforgery";
|
private const string AntiforgeryAttributeName = "asp-antiforgery";
|
||||||
private const string AreaAttributeName = "asp-area";
|
private const string AreaAttributeName = "asp-area";
|
||||||
|
private const string PageAttributeName = "asp-page";
|
||||||
private const string FragmentAttributeName = "asp-fragment";
|
private const string FragmentAttributeName = "asp-fragment";
|
||||||
private const string ControllerAttributeName = "asp-controller";
|
private const string ControllerAttributeName = "asp-controller";
|
||||||
private const string RouteAttributeName = "asp-route";
|
private const string RouteAttributeName = "asp-route";
|
||||||
|
|
@ -72,6 +75,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
[HtmlAttributeName(AreaAttributeName)]
|
[HtmlAttributeName(AreaAttributeName)]
|
||||||
public string Area { get; set; }
|
public string Area { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the page.
|
||||||
|
/// </summary>
|
||||||
|
[HtmlAttributeName(PageAttributeName)]
|
||||||
|
public string Page { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the antiforgery token should be generated.
|
/// Whether the antiforgery token should be generated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -156,6 +165,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
if (Action != null ||
|
if (Action != null ||
|
||||||
Controller != null ||
|
Controller != null ||
|
||||||
Area != null ||
|
Area != null ||
|
||||||
|
Page != null ||
|
||||||
Fragment != null ||
|
Fragment != null ||
|
||||||
Route != null ||
|
Route != null ||
|
||||||
(_routeValues != null && _routeValues.Count > 0))
|
(_routeValues != null && _routeValues.Count > 0))
|
||||||
|
|
@ -163,14 +173,15 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
// User also specified bound attributes we cannot use.
|
// User also specified bound attributes we cannot use.
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
Resources.FormatFormTagHelper_CannotOverrideAction(
|
Resources.FormatFormTagHelper_CannotOverrideAction(
|
||||||
"<form>",
|
|
||||||
HtmlActionAttributeName,
|
HtmlActionAttributeName,
|
||||||
|
"<form>",
|
||||||
|
RouteValuesPrefix,
|
||||||
ActionAttributeName,
|
ActionAttributeName,
|
||||||
ControllerAttributeName,
|
ControllerAttributeName,
|
||||||
FragmentAttributeName,
|
FragmentAttributeName,
|
||||||
AreaAttributeName,
|
AreaAttributeName,
|
||||||
RouteAttributeName,
|
RouteAttributeName,
|
||||||
RouteValuesPrefix));
|
PageAttributeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
// User is using the FormTagHelper like a normal <form> tag. Antiforgery default should be false to
|
// User is using the FormTagHelper like a normal <form> tag. Antiforgery default should be false to
|
||||||
|
|
@ -179,22 +190,33 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IDictionary<string, object> routeValues = null;
|
var routeLink = Route != null;
|
||||||
|
var actionLink = Controller != null || Action != null;
|
||||||
|
var pageLink = Page != null;
|
||||||
|
|
||||||
|
if ((routeLink && actionLink) || (routeLink && pageLink) || (actionLink && pageLink))
|
||||||
|
{
|
||||||
|
var message = string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
Resources.FormatCannotDetermineAttributeFor(HtmlActionAttributeName, "<form>"),
|
||||||
|
RouteAttributeName,
|
||||||
|
ControllerAttributeName + ", " + ActionAttributeName,
|
||||||
|
PageAttributeName);
|
||||||
|
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteValueDictionary routeValues = null;
|
||||||
if (_routeValues != null && _routeValues.Count > 0)
|
if (_routeValues != null && _routeValues.Count > 0)
|
||||||
{
|
{
|
||||||
// Convert from Dictionary<string, string> to Dictionary<string, object>.
|
routeValues = new RouteValueDictionary(_routeValues);
|
||||||
routeValues = new Dictionary<string, object>(_routeValues.Count, StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var routeValue in _routeValues)
|
|
||||||
{
|
|
||||||
routeValues.Add(routeValue.Key, routeValue.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Area != null)
|
if (Area != null)
|
||||||
{
|
{
|
||||||
if (routeValues == null)
|
if (routeValues == null)
|
||||||
{
|
{
|
||||||
routeValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
routeValues = new RouteValueDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unconditionally replace any value from asp-route-area.
|
// Unconditionally replace any value from asp-route-area.
|
||||||
|
|
@ -202,7 +224,27 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
TagBuilder tagBuilder;
|
TagBuilder tagBuilder;
|
||||||
if (Route == null)
|
if (pageLink)
|
||||||
|
{
|
||||||
|
tagBuilder = Generator.GeneratePageForm(
|
||||||
|
ViewContext,
|
||||||
|
Page,
|
||||||
|
routeValues,
|
||||||
|
Fragment,
|
||||||
|
method: null,
|
||||||
|
htmlAttributes: null);
|
||||||
|
}
|
||||||
|
else if (routeLink)
|
||||||
|
{
|
||||||
|
tagBuilder = Generator.GenerateRouteForm(
|
||||||
|
ViewContext,
|
||||||
|
Route,
|
||||||
|
routeValues,
|
||||||
|
Fragment,
|
||||||
|
method: null,
|
||||||
|
htmlAttributes: null);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
tagBuilder = Generator.GenerateForm(
|
tagBuilder = Generator.GenerateForm(
|
||||||
ViewContext,
|
ViewContext,
|
||||||
|
|
@ -213,42 +255,14 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
method: null,
|
method: null,
|
||||||
htmlAttributes: null);
|
htmlAttributes: null);
|
||||||
}
|
}
|
||||||
else if (Action != null || Controller != null)
|
|
||||||
|
output.MergeAttributes(tagBuilder);
|
||||||
|
if (tagBuilder.HasInnerHtml)
|
||||||
{
|
{
|
||||||
// Route and Action or Controller were specified. Can't determine the action attribute.
|
output.PostContent.AppendHtml(tagBuilder.InnerHtml);
|
||||||
throw new InvalidOperationException(
|
|
||||||
Resources.FormatFormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified(
|
|
||||||
"<form>",
|
|
||||||
RouteAttributeName,
|
|
||||||
ActionAttributeName,
|
|
||||||
ControllerAttributeName,
|
|
||||||
HtmlActionAttributeName,
|
|
||||||
FragmentAttributeName));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tagBuilder = Generator.GenerateRouteForm(
|
|
||||||
ViewContext,
|
|
||||||
Route,
|
|
||||||
routeValues,
|
|
||||||
Fragment,
|
|
||||||
method: null,
|
|
||||||
htmlAttributes: null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tagBuilder != null)
|
antiforgeryDefault = !string.Equals(Method, "get", StringComparison.OrdinalIgnoreCase);
|
||||||
{
|
|
||||||
output.MergeAttributes(tagBuilder);
|
|
||||||
if (tagBuilder.HasInnerHtml)
|
|
||||||
{
|
|
||||||
output.PostContent.AppendHtml(tagBuilder.InnerHtml);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.Equals(Method, "get", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
antiforgeryDefault = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Antiforgery ?? antiforgeryDefault)
|
if (Antiforgery ?? antiforgeryDefault)
|
||||||
|
|
|
||||||
|
|
@ -11,21 +11,21 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
= new ResourceManager("Microsoft.AspNetCore.Mvc.TagHelpers.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
= new ResourceManager("Microsoft.AspNetCore.Mvc.TagHelpers.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cannot determine an '{4}' attribute for {0}. An {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.
|
/// Cannot determine the '{0}' attribute for {1}. The following attributes are mutually exclusive:
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified
|
internal static string CannotDetermineAttributeFor
|
||||||
{
|
{
|
||||||
get => GetString("AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified");
|
get => GetString("CannotDetermineAttributeFor");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cannot determine an '{4}' attribute for {0}. An {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.
|
/// Cannot determine the '{0}' attribute for {1}. The following attributes are mutually exclusive:
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatAnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4)
|
internal static string FormatCannotDetermineAttributeFor(object p0, object p1)
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified"), p0, p1, p2, p3, p4);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("CannotDetermineAttributeFor"), p0, p1);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cannot override the '{9}' attribute for {0}. An {0} with a specified '{9}' must not have attributes starting with '{8}' or an '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', or '{7}' 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}', or '{10}' attribute.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string AnchorTagHelper_CannotOverrideHref
|
internal static string AnchorTagHelper_CannotOverrideHref
|
||||||
{
|
{
|
||||||
|
|
@ -33,13 +33,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cannot override the '{9}' attribute for {0}. An {0} with a specified '{9}' must not have attributes starting with '{8}' or an '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', or '{7}' 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}', or '{10}' attribute.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
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)
|
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);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotOverrideHref"), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{7}' or an '{2}', '{3}', '{4}', '{5}', or '{6}' 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormTagHelper_CannotOverrideAction
|
internal static string FormTagHelper_CannotOverrideAction
|
||||||
{
|
{
|
||||||
|
|
@ -47,10 +47,10 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{7}' or an '{2}', '{3}', '{4}', '{5}', or '{6}' 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatFormTagHelper_CannotOverrideAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7)
|
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);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotOverrideAction"), p0, p1, p2, p3, p4, p5, p6, p7, p8);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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}'.
|
/// 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}'.
|
||||||
|
|
@ -136,20 +136,6 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
internal static string FormatTagHelperOutput_AttributeDoesNotExist(object p0, object p1)
|
internal static string FormatTagHelperOutput_AttributeDoesNotExist(object p0, object p1)
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperOutput_AttributeDoesNotExist"), p0, p1);
|
=> 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}', '{3}', or '{5}' attribute.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified
|
|
||||||
{
|
|
||||||
get => GetString("FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatFormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4, object p5)
|
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified"), p0, p1, p2, p3, p4, p5);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The '{0}' property of '{1}' must not be null.
|
/// The '{0}' property of '{1}' must not be null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -178,20 +164,6 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7)
|
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);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6, p7);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Cannot determine a '{4}' attribute for <{0}>. <{0}> elements with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified
|
|
||||||
{
|
|
||||||
get => GetString("FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Cannot determine a '{4}' attribute for <{0}>. <{0}> elements with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatFormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4, object p5)
|
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified"), p0, p1, p2, p3, p4, p5);
|
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -117,14 +117,14 @@
|
||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified" xml:space="preserve">
|
<data name="CannotDetermineAttributeFor" xml:space="preserve">
|
||||||
<value>Cannot determine an '{4}' attribute for {0}. An {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.</value>
|
<value>Cannot determine the '{0}' attribute for {1}. The following attributes are mutually exclusive:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AnchorTagHelper_CannotOverrideHref" xml:space="preserve">
|
<data name="AnchorTagHelper_CannotOverrideHref" xml:space="preserve">
|
||||||
<value>Cannot override the '{9}' attribute for {0}. An {0} with a specified '{9}' must not have attributes starting with '{8}' or an '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', or '{7}' attribute.</value>
|
<value>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.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FormTagHelper_CannotOverrideAction" xml:space="preserve">
|
<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 '{7}' or an '{2}', '{3}', '{4}', '{5}', or '{6}' attribute.</value>
|
<value>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.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InputTagHelper_InvalidExpressionResult" xml:space="preserve">
|
<data name="InputTagHelper_InvalidExpressionResult" xml:space="preserve">
|
||||||
<value>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}'.</value>
|
<value>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}'.</value>
|
||||||
|
|
@ -144,16 +144,10 @@
|
||||||
<data name="TagHelperOutput_AttributeDoesNotExist" xml:space="preserve">
|
<data name="TagHelperOutput_AttributeDoesNotExist" xml:space="preserve">
|
||||||
<value>The attribute '{0}' does not exist in the {1}.</value>
|
<value>The attribute '{0}' does not exist in the {1}.</value>
|
||||||
</data>
|
</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}', '{3}', or '{5}' attribute.</value>
|
|
||||||
</data>
|
|
||||||
<data name="PropertyOfTypeCannotBeNull" xml:space="preserve">
|
<data name="PropertyOfTypeCannotBeNull" xml:space="preserve">
|
||||||
<value>The '{0}' property of '{1}' must not be null.</value>
|
<value>The '{0}' property of '{1}' must not be null.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FormActionTagHelper_CannotOverrideFormAction" xml:space="preserve">
|
<data name="FormActionTagHelper_CannotOverrideFormAction" xml:space="preserve">
|
||||||
<value>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.</value>
|
<value>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.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified" xml:space="preserve">
|
|
||||||
<value>Cannot determine a '{4}' attribute for <{0}>. <{0}> elements with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -142,6 +142,32 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
return GenerateLink(linkText, url, htmlAttributes);
|
return GenerateLink(linkText, url, htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual TagBuilder GeneratePageLink(
|
||||||
|
ViewContext viewContext,
|
||||||
|
string linkText,
|
||||||
|
string pageName,
|
||||||
|
string protocol,
|
||||||
|
string hostname,
|
||||||
|
string fragment,
|
||||||
|
object routeValues,
|
||||||
|
object htmlAttributes)
|
||||||
|
{
|
||||||
|
if (viewContext == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(viewContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linkText == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(linkText));
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlHelper = _urlHelperFactory.GetUrlHelper(viewContext);
|
||||||
|
var url = urlHelper.Page(pageName, routeValues, protocol, hostname, fragment);
|
||||||
|
return GenerateLink(linkText, url, htmlAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual IHtmlContent GenerateAntiforgery(ViewContext viewContext)
|
public virtual IHtmlContent GenerateAntiforgery(ViewContext viewContext)
|
||||||
{
|
{
|
||||||
|
|
@ -281,6 +307,52 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
return GenerateFormCore(viewContext, action, method, htmlAttributes);
|
return GenerateFormCore(viewContext, action, method, htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual TagBuilder GeneratePageForm(
|
||||||
|
ViewContext viewContext,
|
||||||
|
string pageName,
|
||||||
|
object routeValues,
|
||||||
|
string fragment,
|
||||||
|
string method,
|
||||||
|
object htmlAttributes)
|
||||||
|
{
|
||||||
|
if (viewContext == null)
|
||||||
|
{
|
||||||
|
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, values: routeValues,protocol: null, host: null, fragment: fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenerateFormCore(viewContext, action, method, htmlAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public TagBuilder GenerateRouteForm(
|
public TagBuilder GenerateRouteForm(
|
||||||
ViewContext viewContext,
|
ViewContext viewContext,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,38 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
object routeValues,
|
object routeValues,
|
||||||
object htmlAttributes);
|
object htmlAttributes);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate a <a> element for a link to an action.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="viewContext">The <see cref="ViewContext"/> instance for the current scope.</param>
|
||||||
|
/// <param name="linkText">The text to insert inside the element.</param>
|
||||||
|
/// <param name="pageName">The page name.</param>
|
||||||
|
/// <param name="protocol">The protocol (scheme) for the generated link.</param>
|
||||||
|
/// <param name="hostname">The hostname for the generated link.</param>
|
||||||
|
/// <param name="fragment">The fragment for the genrated link.</param>
|
||||||
|
/// <param name="routeValues">
|
||||||
|
/// An <see cref="object"/> that contains the parameters for a route. The parameters are retrieved through
|
||||||
|
/// reflection by examining the properties of the <see cref="object"/>. This <see cref="object"/> is typically
|
||||||
|
/// created using <see cref="object"/> initializer syntax. Alternatively, an
|
||||||
|
/// <see cref="IDictionary{String, Object}"/> instance containing the route parameters.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="htmlAttributes">
|
||||||
|
/// An <see cref="object"/> that contains the HTML attributes for the element. Alternatively, an
|
||||||
|
/// <see cref="IDictionary{String, Object}"/> instance containing the HTML attributes.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="TagBuilder"/> instance for the <a> element.
|
||||||
|
/// </returns>
|
||||||
|
TagBuilder GeneratePageLink(
|
||||||
|
ViewContext viewContext,
|
||||||
|
string linkText,
|
||||||
|
string pageName,
|
||||||
|
string protocol,
|
||||||
|
string hostname,
|
||||||
|
string fragment,
|
||||||
|
object routeValues,
|
||||||
|
object htmlAttributes);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate an <input type="hidden".../> element containing an antiforgery token.
|
/// Generate an <input type="hidden".../> element containing an antiforgery token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -124,6 +156,35 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
string method,
|
string method,
|
||||||
object htmlAttributes);
|
object htmlAttributes);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate a <form> element. When the user submits the form, the page with name
|
||||||
|
/// <paramref name="pageName"/> will process the request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="viewContext">A <see cref="ViewContext"/> instance for the current scope.</param>
|
||||||
|
/// <param name="pageName">The name of the action method.</param>
|
||||||
|
/// <param name="routeValues">
|
||||||
|
/// An <see cref="object"/> that contains the parameters for a route. The parameters are retrieved through
|
||||||
|
/// reflection by examining the properties of the <see cref="object"/>. This <see cref="object"/> is typically
|
||||||
|
/// created using <see cref="object"/> initializer syntax. Alternatively, an
|
||||||
|
/// <see cref="IDictionary{String, Object}"/> instance containing the route parameters.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="fragment">The url fragment.</param>
|
||||||
|
/// <param name="method">The HTTP method for processing the form, either GET or POST.</param>
|
||||||
|
/// <param name="htmlAttributes">
|
||||||
|
/// An <see cref="object"/> that contains the HTML attributes for the element. Alternatively, an
|
||||||
|
/// <see cref="IDictionary{String, Object}"/> instance containing the HTML attributes.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="TagBuilder"/> instance for the </form> element.
|
||||||
|
/// </returns>
|
||||||
|
TagBuilder GeneratePageForm(
|
||||||
|
ViewContext viewContext,
|
||||||
|
string pageName,
|
||||||
|
object routeValues,
|
||||||
|
string fragment,
|
||||||
|
string method,
|
||||||
|
object htmlAttributes);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate a <form> element. The route with name <paramref name="routeName"/> generates the
|
/// Generate a <form> element. The route with name <paramref name="routeName"/> generates the
|
||||||
/// <form>'s <c>action</c> attribute value.
|
/// <form>'s <c>action</c> attribute value.
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||||
|
|
||||||
var urlHelper = GetUrlHelper(actionContext, returnValue: null);
|
var urlHelper = GetUrlHelper(actionContext, returnValue: null);
|
||||||
var result = new RedirectToPageResult("some-page", new Dictionary<string, object>())
|
var result = new RedirectToPageResult("/some-page", new Dictionary<string, object>())
|
||||||
{
|
{
|
||||||
UrlHelper = urlHelper,
|
UrlHelper = urlHelper,
|
||||||
};
|
};
|
||||||
|
|
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await ExceptionAssert.ThrowsAsync<InvalidOperationException>(
|
await ExceptionAssert.ThrowsAsync<InvalidOperationException>(
|
||||||
() => result.ExecuteResultAsync(actionContext),
|
() => result.ExecuteResultAsync(actionContext),
|
||||||
"No page named 'some-page' matches the supplied values.");
|
"No page named '/some-page' matches the supplied values.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
new ActionDescriptor());
|
new ActionDescriptor());
|
||||||
|
|
||||||
var urlHelper = GetUrlHelper(actionContext, expectedUrl);
|
var urlHelper = GetUrlHelper(actionContext, expectedUrl);
|
||||||
var result = new RedirectToPageResult("MyPage", new { id = 10, test = "value" }, permanentRedirect)
|
var result = new RedirectToPageResult("/MyPage", new { id = 10, test = "value" }, permanentRedirect)
|
||||||
{
|
{
|
||||||
UrlHelper = urlHelper,
|
UrlHelper = urlHelper,
|
||||||
};
|
};
|
||||||
|
|
@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
.Callback((UrlRouteContext c) => context = c)
|
.Callback((UrlRouteContext c) => context = c)
|
||||||
.Returns("some-value");
|
.Returns("some-value");
|
||||||
var values = new { test = "test-value" };
|
var values = new { test = "test-value" };
|
||||||
var result = new RedirectToPageResult("MyPage", values, true, "test-fragment")
|
var result = new RedirectToPageResult("/MyPage", values, true, "test-fragment")
|
||||||
{
|
{
|
||||||
UrlHelper = urlHelper.Object,
|
UrlHelper = urlHelper.Object,
|
||||||
Protocol = "ftp",
|
Protocol = "ftp",
|
||||||
|
|
@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
value =>
|
value =>
|
||||||
{
|
{
|
||||||
Assert.Equal("page", value.Key);
|
Assert.Equal("page", value.Key);
|
||||||
Assert.Equal("MyPage", value.Value);
|
Assert.Equal("/MyPage", value.Value);
|
||||||
});
|
});
|
||||||
Assert.Equal("ftp", context.Protocol);
|
Assert.Equal("ftp", context.Protocol);
|
||||||
Assert.Equal("test-fragment", context.Fragment);
|
Assert.Equal("test-fragment", context.Fragment);
|
||||||
|
|
|
||||||
|
|
@ -903,6 +903,54 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore._InjectedP
|
||||||
Assert.Equal(expected, response.Headers.Location.ToString());
|
Assert.Equal(expected, response.Headers.Location.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TagHelpers_SupportSiblingRoutes()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected =
|
||||||
|
@"<form method=""post"" action=""/Pages/TagHelper/CrossPost""></form>
|
||||||
|
<a href=""/Pages/TagHelper/SelfPost/12"" />
|
||||||
|
<input type=""image"" formaction=""/Pages/TagHelper/CrossPost#my-fragment"" />";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetStringAsync("/Pages/TagHelper/SiblingLinks");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, response.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TagHelpers_SupportSubDirectoryRoutes()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected =
|
||||||
|
@"<form method=""post"" action=""/Pages/TagHelper/SubDir/SubDirPage""></form>
|
||||||
|
<a href=""/Pages/TagHelper/SubDir/SubDirPage/12"" />
|
||||||
|
<input type=""image"" formaction=""/Pages/TagHelper/SubDir/SubDirPage#my-fragment"" />";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetStringAsync("/Pages/TagHelper/SubDirectoryLinks");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, response.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TagHelpers_SupportsPathNavigation()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected =
|
||||||
|
@"<form method=""post"" action=""/HelloWorld""></form>
|
||||||
|
<a href=""/Pages/Redirects/RedirectToIndex"" />
|
||||||
|
<input type=""image"" formaction=""/Pages/Admin#my-fragment"" />";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetStringAsync("/Pages/TagHelper/PathTraversalLinks");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, response.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
private async Task AddAntiforgeryHeaders(HttpRequestMessage request)
|
private async Task AddAntiforgeryHeaders(HttpRequestMessage request)
|
||||||
{
|
{
|
||||||
var getResponse = await Client.GetAsync(request.RequestUri);
|
var getResponse = await Client.GetAsync(request.RequestUri);
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,48 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal(expected, response.Trim());
|
Assert.Equal(expected, response.Trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FormTagHelper_WithPage_GeneratesLinksToSelf()
|
||||||
|
{
|
||||||
|
//Arrange
|
||||||
|
var expected = "<form method=\"POST\" action=\"/TagHelper/SelfPost/10\">";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetStringAsync("/TagHelper/SelfPost");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Contains(expected, response.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FormTagHelper_WithPage_AllowsPostingToAnotherPage()
|
||||||
|
{
|
||||||
|
//Arrange
|
||||||
|
var expected = "<form method=\"POST\" action=\"/TagHelper/SelfPost/10\">";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetStringAsync("/TagHelper/CrossPost");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Contains(expected, response.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FormActionTagHelper_WithPage_AllowsPostingToAnotherPage()
|
||||||
|
{
|
||||||
|
//Arrange
|
||||||
|
var expected =
|
||||||
|
@"<button formaction=""/TagHelper/CrossPost/10"" />
|
||||||
|
<input type=""submit"" formaction=""/TagHelper/CrossPost/10"" />
|
||||||
|
<input type=""image"" formaction=""/TagHelper/CrossPost/10"" />";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetStringAsync("/TagHelper/FormAction");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, response.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RedirectFromPage_RedirectsToPathWithoutIndexSegment()
|
public async Task RedirectFromPage_RedirectsToPathWithoutIndexSegment()
|
||||||
{
|
{
|
||||||
|
|
@ -170,5 +212,18 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||||
Assert.Equal(expected, response.Headers.Location.ToString());
|
Assert.Equal(expected, response.Headers.Location.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PageRoute_UsingDefaultPageNameToRoute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = @"<a href=""/Routes/Sibling/10"">Link</a>";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.GetStringAsync("/Routes/RouteUsingDefaultName");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, response.Trim());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -366,6 +366,55 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
Assert.Empty(output.Content.GetContent());
|
Assert.Empty(output.Content.GetContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_AddsPageToRouteValuesAndCallsPageLinkWithExpectedParameters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new TagHelperContext(
|
||||||
|
tagName: "a",
|
||||||
|
allAttributes: new TagHelperAttributeList(
|
||||||
|
Enumerable.Empty<TagHelperAttribute>()),
|
||||||
|
items: new Dictionary<object, object>(),
|
||||||
|
uniqueId: "test");
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
"a",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) =>
|
||||||
|
{
|
||||||
|
var tagHelperContent = new DefaultTagHelperContent();
|
||||||
|
tagHelperContent.SetContent("Something");
|
||||||
|
return Task.FromResult<TagHelperContent>(tagHelperContent);
|
||||||
|
});
|
||||||
|
output.Content.SetContent(string.Empty);
|
||||||
|
|
||||||
|
var generator = new Mock<IHtmlGenerator>();
|
||||||
|
generator
|
||||||
|
.Setup(mock => mock.GeneratePageLink(
|
||||||
|
It.IsAny<ViewContext>(),
|
||||||
|
string.Empty,
|
||||||
|
"/User/Home/Index",
|
||||||
|
"http",
|
||||||
|
"contoso.com",
|
||||||
|
"hello=world",
|
||||||
|
It.IsAny<object>(),
|
||||||
|
null))
|
||||||
|
.Returns(new TagBuilder("a"))
|
||||||
|
.Verifiable();
|
||||||
|
var anchorTagHelper = new AnchorTagHelper(generator.Object)
|
||||||
|
{
|
||||||
|
Page = "/User/Home/Index",
|
||||||
|
Fragment = "hello=world",
|
||||||
|
Host = "contoso.com",
|
||||||
|
Protocol = "http",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await anchorTagHelper.ProcessAsync(context, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
generator.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("Action")]
|
[InlineData("Action")]
|
||||||
[InlineData("Controller")]
|
[InlineData("Controller")]
|
||||||
|
|
@ -374,6 +423,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
[InlineData("Host")]
|
[InlineData("Host")]
|
||||||
[InlineData("Fragment")]
|
[InlineData("Fragment")]
|
||||||
[InlineData("asp-route-")]
|
[InlineData("asp-route-")]
|
||||||
|
[InlineData("Page")]
|
||||||
public async Task ProcessAsync_ThrowsIfHrefConflictsWithBoundAttributes(string propertyName)
|
public async Task ProcessAsync_ThrowsIfHrefConflictsWithBoundAttributes(string propertyName)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -399,9 +449,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
var expectedErrorMessage = "Cannot override the 'href' attribute for <a>. An <a> with a specified " +
|
var expectedErrorMessage = "Cannot override the 'href' attribute for <a>. An <a> with a specified " +
|
||||||
"'href' must not have attributes starting with 'asp-route-' or an " +
|
"'href' must not have attributes starting with 'asp-route-' or an " +
|
||||||
"'asp-action', 'asp-controller', 'asp-area', 'asp-route', 'asp-protocol', 'asp-host', or " +
|
"'asp-action', 'asp-controller', 'asp-area', 'asp-route', 'asp-protocol', 'asp-host', " +
|
||||||
"'asp-fragment' attribute.";
|
"'asp-fragment', or 'asp-page' attribute.";
|
||||||
|
|
||||||
var context = new TagHelperContext(
|
var context = new TagHelperContext(
|
||||||
tagName: "test",
|
tagName: "test",
|
||||||
|
|
@ -436,8 +486,88 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
"a",
|
"a",
|
||||||
attributes: new TagHelperAttributeList(),
|
attributes: new TagHelperAttributeList(),
|
||||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||||
var expectedErrorMessage = "Cannot determine an 'href' attribute for <a>. An <a> with a specified " +
|
var expectedErrorMessage = string.Join(
|
||||||
"'asp-route' must not have an 'asp-action' or 'asp-controller' attribute.";
|
Environment.NewLine,
|
||||||
|
"Cannot determine the 'href' attribute for <a>. The following attributes are mutually exclusive:",
|
||||||
|
"asp-route",
|
||||||
|
"asp-controller, asp-action",
|
||||||
|
"asp-page");
|
||||||
|
|
||||||
|
var context = new TagHelperContext(
|
||||||
|
tagName: "test",
|
||||||
|
allAttributes: new TagHelperAttributeList(
|
||||||
|
Enumerable.Empty<TagHelperAttribute>()),
|
||||||
|
items: new Dictionary<object, object>(),
|
||||||
|
uniqueId: "test");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
|
() => anchorTagHelper.ProcessAsync(context, output));
|
||||||
|
|
||||||
|
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_ThrowsIfRouteAndPageProvided()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
|
||||||
|
|
||||||
|
var anchorTagHelper = new AnchorTagHelper(htmlGenerator)
|
||||||
|
{
|
||||||
|
Route = "Default",
|
||||||
|
Page = "Page",
|
||||||
|
};
|
||||||
|
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
"a",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||||
|
var expectedErrorMessage = string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
"Cannot determine the 'href' attribute for <a>. The following attributes are mutually exclusive:",
|
||||||
|
"asp-route",
|
||||||
|
"asp-controller, asp-action",
|
||||||
|
"asp-page");
|
||||||
|
|
||||||
|
var context = new TagHelperContext(
|
||||||
|
tagName: "test",
|
||||||
|
allAttributes: new TagHelperAttributeList(
|
||||||
|
Enumerable.Empty<TagHelperAttribute>()),
|
||||||
|
items: new Dictionary<object, object>(),
|
||||||
|
uniqueId: "test");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
|
() => anchorTagHelper.ProcessAsync(context, output));
|
||||||
|
|
||||||
|
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_ThrowsIfActionAndPageProvided()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
|
||||||
|
|
||||||
|
var anchorTagHelper = new AnchorTagHelper(htmlGenerator)
|
||||||
|
{
|
||||||
|
Action = "Action",
|
||||||
|
Page = "Page",
|
||||||
|
};
|
||||||
|
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
"a",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||||
|
var expectedErrorMessage = string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
"Cannot determine the 'href' attribute for <a>. The following attributes are mutually exclusive:",
|
||||||
|
"asp-route",
|
||||||
|
"asp-controller, asp-action",
|
||||||
|
"asp-page");
|
||||||
|
|
||||||
var context = new TagHelperContext(
|
var context = new TagHelperContext(
|
||||||
tagName: "test",
|
tagName: "test",
|
||||||
|
|
|
||||||
|
|
@ -510,8 +510,88 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
tagName,
|
tagName,
|
||||||
attributes: new TagHelperAttributeList(),
|
attributes: new TagHelperAttributeList(),
|
||||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||||
var expectedErrorMessage = $"Cannot determine a 'formaction' attribute for <{tagName}>. <{tagName}> " +
|
var expectedErrorMessage = string.Join(
|
||||||
"elements with a specified 'asp-route' must not have an 'asp-action', 'asp-controller', or 'asp-fragment' attribute.";
|
Environment.NewLine,
|
||||||
|
$"Cannot determine the 'formaction' attribute for <{tagName}>. The following attributes are mutually exclusive:",
|
||||||
|
"asp-route",
|
||||||
|
"asp-controller, asp-action",
|
||||||
|
"asp-page");
|
||||||
|
|
||||||
|
var context = new TagHelperContext(
|
||||||
|
tagName: "form-action",
|
||||||
|
allAttributes: new TagHelperAttributeList(
|
||||||
|
Enumerable.Empty<TagHelperAttribute>()),
|
||||||
|
items: new Dictionary<object, object>(),
|
||||||
|
uniqueId: "test");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => tagHelper.ProcessAsync(context, output));
|
||||||
|
|
||||||
|
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("button")]
|
||||||
|
[InlineData("submit")]
|
||||||
|
public async Task ProcessAsync_ThrowsIfRouteAndPageProvided(string tagName)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var urlHelperFactory = new Mock<IUrlHelperFactory>().Object;
|
||||||
|
|
||||||
|
var tagHelper = new FormActionTagHelper(urlHelperFactory)
|
||||||
|
{
|
||||||
|
Route = "Default",
|
||||||
|
Page = "Page",
|
||||||
|
};
|
||||||
|
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
tagName,
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||||
|
var expectedErrorMessage = string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
$"Cannot determine the 'formaction' attribute for <{tagName}>. The following attributes are mutually exclusive:",
|
||||||
|
"asp-route",
|
||||||
|
"asp-controller, asp-action",
|
||||||
|
"asp-page");
|
||||||
|
|
||||||
|
var context = new TagHelperContext(
|
||||||
|
tagName: "form-action",
|
||||||
|
allAttributes: new TagHelperAttributeList(
|
||||||
|
Enumerable.Empty<TagHelperAttribute>()),
|
||||||
|
items: new Dictionary<object, object>(),
|
||||||
|
uniqueId: "test");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => tagHelper.ProcessAsync(context, output));
|
||||||
|
|
||||||
|
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("button")]
|
||||||
|
[InlineData("submit")]
|
||||||
|
public async Task ProcessAsync_ThrowsIfActionAndPageProvided(string tagName)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var urlHelperFactory = new Mock<IUrlHelperFactory>().Object;
|
||||||
|
|
||||||
|
var tagHelper = new FormActionTagHelper(urlHelperFactory)
|
||||||
|
{
|
||||||
|
Action = "Default",
|
||||||
|
Page = "Page",
|
||||||
|
};
|
||||||
|
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
tagName,
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||||
|
var expectedErrorMessage = string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
$"Cannot determine the 'formaction' attribute for <{tagName}>. The following attributes are mutually exclusive:",
|
||||||
|
"asp-route",
|
||||||
|
"asp-controller, asp-action",
|
||||||
|
"asp-page");
|
||||||
|
|
||||||
var context = new TagHelperContext(
|
var context = new TagHelperContext(
|
||||||
tagName: "form-action",
|
tagName: "form-action",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Html;
|
using Microsoft.AspNetCore.Html;
|
||||||
|
|
@ -201,8 +200,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
// Fixes Roslyn bug with lambdas
|
// Fixes Roslyn bug with lambdas
|
||||||
generator.ToString();
|
generator.ToString();
|
||||||
|
|
||||||
var routeValueDictionary = (Dictionary<string, object>)routeValues;
|
var routeValueDictionary = Assert.IsType<RouteValueDictionary>(routeValues);
|
||||||
|
|
||||||
Assert.Equal(2, routeValueDictionary.Count);
|
Assert.Equal(2, routeValueDictionary.Count);
|
||||||
var routeValue = Assert.Single(routeValueDictionary, attr => attr.Key.Equals("val"));
|
var routeValue = Assert.Single(routeValueDictionary, attr => attr.Key.Equals("val"));
|
||||||
Assert.Equal("hello", routeValue.Value);
|
Assert.Equal("hello", routeValue.Value);
|
||||||
|
|
@ -552,7 +550,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
.Setup(mock => mock.GenerateRouteForm(
|
.Setup(mock => mock.GenerateRouteForm(
|
||||||
viewContext,
|
viewContext,
|
||||||
"Default",
|
"Default",
|
||||||
It.Is<Dictionary<string, object>>(m => string.Equals(m["name"], "value")),
|
It.Is<RouteValueDictionary>(m => string.Equals(m["name"], "value")),
|
||||||
null,
|
null,
|
||||||
null))
|
null))
|
||||||
.Returns(new TagBuilder("form"))
|
.Returns(new TagBuilder("form"))
|
||||||
|
|
@ -582,6 +580,50 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
Assert.Empty(output.PostElement.GetContent());
|
Assert.Empty(output.PostElement.GetContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_InvokesGeneratePageForm()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var viewContext = CreateViewContext();
|
||||||
|
var context = new TagHelperContext(
|
||||||
|
tagName: "form",
|
||||||
|
allAttributes: new TagHelperAttributeList(
|
||||||
|
Enumerable.Empty<TagHelperAttribute>()),
|
||||||
|
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,
|
||||||
|
"/Home/Admin/Post",
|
||||||
|
null,
|
||||||
|
"hello-world",
|
||||||
|
null,
|
||||||
|
null))
|
||||||
|
.Returns(new TagBuilder("form"))
|
||||||
|
.Verifiable();
|
||||||
|
var formTagHelper = new FormTagHelper(generator.Object)
|
||||||
|
{
|
||||||
|
Antiforgery = false,
|
||||||
|
ViewContext = viewContext,
|
||||||
|
Page = "/Home/Admin/Post",
|
||||||
|
Fragment = "hello-world",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
await formTagHelper.ProcessAsync(context, output);
|
||||||
|
generator.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(true, "<input />")]
|
[InlineData(true, "<input />")]
|
||||||
[InlineData(false, "")]
|
[InlineData(false, "")]
|
||||||
|
|
@ -638,6 +680,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("Action")]
|
[InlineData("Action")]
|
||||||
[InlineData("Controller")]
|
[InlineData("Controller")]
|
||||||
|
[InlineData("Page")]
|
||||||
[InlineData("asp-route-")]
|
[InlineData("asp-route-")]
|
||||||
public async Task ProcessAsync_ThrowsIfActionConflictsWithBoundAttributes(string propertyName)
|
public async Task ProcessAsync_ThrowsIfActionConflictsWithBoundAttributes(string propertyName)
|
||||||
{
|
{
|
||||||
|
|
@ -660,8 +703,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
var expectedErrorMessage = "Cannot override the 'action' attribute for <form>. A <form> with a specified " +
|
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 " +
|
"'action' must not have attributes starting with 'asp-route-' or an " +
|
||||||
"'asp-action', 'asp-controller', 'asp-fragment', 'asp-area', or 'asp-route' attribute.";
|
"'asp-action', 'asp-controller', 'asp-fragment', 'asp-area', 'asp-route' or 'asp-page' attribute.";
|
||||||
|
|
||||||
var context = new TagHelperContext(
|
var context = new TagHelperContext(
|
||||||
tagName: "form",
|
tagName: "form",
|
||||||
|
|
@ -692,8 +735,80 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
"form",
|
"form",
|
||||||
attributes: new TagHelperAttributeList(),
|
attributes: new TagHelperAttributeList(),
|
||||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||||
var expectedErrorMessage = "Cannot determine an 'action' attribute for <form>. A <form> with a specified " +
|
var expectedErrorMessage = string.Join(
|
||||||
"'asp-route' must not have an 'asp-action', 'asp-controller', or 'asp-fragment' attribute.";
|
Environment.NewLine,
|
||||||
|
"Cannot determine the 'action' attribute for <form>. The following attributes are mutually exclusive:",
|
||||||
|
"asp-route",
|
||||||
|
"asp-controller, asp-action",
|
||||||
|
"asp-page");
|
||||||
|
|
||||||
|
var context = new TagHelperContext(
|
||||||
|
tagName: "form",
|
||||||
|
allAttributes: new TagHelperAttributeList(
|
||||||
|
Enumerable.Empty<TagHelperAttribute>()),
|
||||||
|
items: new Dictionary<object, object>(),
|
||||||
|
uniqueId: "test");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
|
() => formTagHelper.ProcessAsync(context, output));
|
||||||
|
|
||||||
|
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_ThrowsIfRouteAndPageProvided()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var formTagHelper = new FormTagHelper(new TestableHtmlGenerator(new EmptyModelMetadataProvider()))
|
||||||
|
{
|
||||||
|
Route = "Default",
|
||||||
|
Page = "Page",
|
||||||
|
};
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
"form",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||||
|
var expectedErrorMessage = string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
"Cannot determine the 'action' attribute for <form>. The following attributes are mutually exclusive:",
|
||||||
|
"asp-route",
|
||||||
|
"asp-controller, asp-action",
|
||||||
|
"asp-page");
|
||||||
|
|
||||||
|
var context = new TagHelperContext(
|
||||||
|
tagName: "form",
|
||||||
|
allAttributes: new TagHelperAttributeList(
|
||||||
|
Enumerable.Empty<TagHelperAttribute>()),
|
||||||
|
items: new Dictionary<object, object>(),
|
||||||
|
uniqueId: "test");
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
|
() => formTagHelper.ProcessAsync(context, output));
|
||||||
|
|
||||||
|
Assert.Equal(expectedErrorMessage, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessAsync_ThrowsIfActionAndPageProvided()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var formTagHelper = new FormTagHelper(new TestableHtmlGenerator(new EmptyModelMetadataProvider()))
|
||||||
|
{
|
||||||
|
Action = "Default",
|
||||||
|
Page = "Page",
|
||||||
|
};
|
||||||
|
var output = new TagHelperOutput(
|
||||||
|
"form",
|
||||||
|
attributes: new TagHelperAttributeList(),
|
||||||
|
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||||
|
var expectedErrorMessage = string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
"Cannot determine the 'action' attribute for <form>. The following attributes are mutually exclusive:",
|
||||||
|
"asp-route",
|
||||||
|
"asp-controller, asp-action",
|
||||||
|
"asp-page");
|
||||||
|
|
||||||
var context = new TagHelperContext(
|
var context = new TagHelperContext(
|
||||||
tagName: "form",
|
tagName: "form",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
@page
|
||||||
|
<a asp-page="/Routes/Sibling" asp-route-id="10">Link</a>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
@page "{id?}"
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
@page "{id:int?}"
|
||||||
|
|
||||||
|
<form method="POST" asp-page="/TagHelper/SelfPost" asp-route-id="10">
|
||||||
|
<input type="submit" value="Submit" />
|
||||||
|
</form>
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
@page
|
||||||
|
<button asp-page="/TagHelper/CrossPost" asp-route-id="10" />
|
||||||
|
<input type="submit" asp-page="/TagHelper/CrossPost" asp-route-id="10" />
|
||||||
|
<input type="image" asp-page="/TagHelper/CrossPost" asp-route-id="10" />
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
@page
|
||||||
|
|
||||||
|
<form method="post" asp-page="../../HelloWorld" asp-antiforgery="false"></form>
|
||||||
|
<a asp-page="../Redirects/Index" asp-route-formaction="RedirectToIndex" />
|
||||||
|
<input type="image" asp-page="../Admin/Index" asp-fragment="my-fragment" />
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
@page "{id:int?}"
|
||||||
|
|
||||||
|
<form method="POST" asp-page="" asp-route-id="10">
|
||||||
|
<input type="submit" value="Submit" />
|
||||||
|
</form>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
@page
|
||||||
|
|
||||||
|
<form method="post" asp-page="CrossPost" asp-antiforgery="false"></form>
|
||||||
|
<a asp-page="SelfPost" asp-route-id="12" />
|
||||||
|
<input type="image" asp-page="CrossPost" asp-fragment="my-fragment" />
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
@page "{id?}"
|
||||||
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
@page
|
||||||
|
|
||||||
|
<form method="post" asp-page="SubDir/SubDirPage" asp-antiforgery="false"></form>
|
||||||
|
<a asp-page="SubDir/SubDirPage" asp-route-id="12" />
|
||||||
|
<input type="image" asp-page="SubDir/SubDirPage" asp-fragment="my-fragment" />
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
|
||||||
Loading…
Reference in New Issue