parent
f568d3c2bc
commit
ca017eced2
|
|
@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
[HtmlTargetElement("a", Attributes = ActionAttributeName)]
|
||||
[HtmlTargetElement("a", Attributes = ControllerAttributeName)]
|
||||
[HtmlTargetElement("a", Attributes = AreaAttributeName)]
|
||||
[HtmlTargetElement("a", Attributes = PageAttributeName)]
|
||||
[HtmlTargetElement("a", Attributes = FragmentAttributeName)]
|
||||
[HtmlTargetElement("a", Attributes = HostAttributeName)]
|
||||
[HtmlTargetElement("a", Attributes = ProtocolAttributeName)]
|
||||
|
|
@ -27,6 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
private const string ActionAttributeName = "asp-action";
|
||||
private const string ControllerAttributeName = "asp-controller";
|
||||
private const string AreaAttributeName = "asp-area";
|
||||
private const string PageAttributeName = "asp-page";
|
||||
private const string FragmentAttributeName = "asp-fragment";
|
||||
private const string HostAttributeName = "asp-host";
|
||||
private const string ProtocolAttributeName = "asp-protocol";
|
||||
|
|
@ -59,24 +61,40 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
/// <summary>
|
||||
/// The name of the action method.
|
||||
/// </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)]
|
||||
public string Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the controller.
|
||||
/// </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)]
|
||||
public string Controller { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the area.
|
||||
/// </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)]
|
||||
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>
|
||||
/// The protocol for the URL, such as "http" or "https".
|
||||
/// </summary>
|
||||
|
|
@ -99,7 +117,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
/// Name of the route.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
[HtmlAttributeName(RouteAttributeName)]
|
||||
public string Route { get; set; }
|
||||
|
|
@ -134,12 +153,6 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
|
||||
/// <inheritdoc />
|
||||
/// <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)
|
||||
{
|
||||
if (context == null)
|
||||
|
|
@ -158,6 +171,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
if (Action != null ||
|
||||
Controller != null ||
|
||||
Area != null ||
|
||||
Page != null ||
|
||||
Route != null ||
|
||||
Protocol != 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.
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatAnchorTagHelper_CannotOverrideHref(
|
||||
Href,
|
||||
"<a>",
|
||||
RouteValuesPrefix,
|
||||
ActionAttributeName,
|
||||
ControllerAttributeName,
|
||||
AreaAttributeName,
|
||||
|
|
@ -175,72 +191,84 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
ProtocolAttributeName,
|
||||
HostAttributeName,
|
||||
FragmentAttributeName,
|
||||
RouteValuesPrefix,
|
||||
Href));
|
||||
PageAttributeName));
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
tagBuilder = Generator.GenerateActionLink(
|
||||
ViewContext,
|
||||
linkText: string.Empty,
|
||||
actionName: Action,
|
||||
controllerName: Controller,
|
||||
protocol: Protocol,
|
||||
hostname: Host,
|
||||
fragment: Fragment,
|
||||
routeValues: routeValues,
|
||||
htmlAttributes: null);
|
||||
}
|
||||
|
||||
output.MergeAttributes(tagBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
[HtmlTargetElement("button", Attributes = ActionAttributeName)]
|
||||
[HtmlTargetElement("button", Attributes = ControllerAttributeName)]
|
||||
[HtmlTargetElement("button", Attributes = AreaAttributeName)]
|
||||
[HtmlTargetElement("button", Attributes = PageAttributeName)]
|
||||
[HtmlTargetElement("button", Attributes = FragmentAttributeName)]
|
||||
[HtmlTargetElement("button", Attributes = RouteAttributeName)]
|
||||
[HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)]
|
||||
|
|
@ -25,6 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
[HtmlTargetElement("input", Attributes = ImageActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
|
||||
[HtmlTargetElement("input", Attributes = ImageControllerAttributeSelector, 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 = ImageRouteAttributeSelector, 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 = SubmitControllerAttributeSelector, 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 = SubmitRouteAttributeSelector, 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 AreaAttributeName = "asp-area";
|
||||
private const string ControllerAttributeName = "asp-controller";
|
||||
private const string PageAttributeName = "asp-page";
|
||||
private const string FragmentAttributeName = "asp-fragment";
|
||||
private const string RouteAttributeName = "asp-route";
|
||||
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 ImageActionAttributeSelector = ImageTypeSelector + ActionAttributeName;
|
||||
private const string ImageAreaAttributeSelector = ImageTypeSelector + AreaAttributeName;
|
||||
private const string ImagePageAttributeSelector = ImageTypeSelector + PageAttributeName;
|
||||
private const string ImageFragmentAttributeSelector = ImageTypeSelector + FragmentAttributeName;
|
||||
private const string ImageControllerAttributeSelector = ImageTypeSelector + ControllerAttributeName;
|
||||
private const string ImageRouteAttributeSelector = ImageTypeSelector + RouteAttributeName;
|
||||
|
|
@ -59,6 +64,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
private const string SubmitTypeSelector = "[type=submit], ";
|
||||
private const string SubmitActionAttributeSelector = SubmitTypeSelector + ActionAttributeName;
|
||||
private const string SubmitAreaAttributeSelector = SubmitTypeSelector + AreaAttributeName;
|
||||
private const string SubmitPageAttributeSelector = SubmitTypeSelector + PageAttributeName;
|
||||
private const string SubmitFragmentAttributeSelector = SubmitTypeSelector + FragmentAttributeName;
|
||||
private const string SubmitControllerAttributeSelector = SubmitTypeSelector + ControllerAttributeName;
|
||||
private const string SubmitRouteAttributeSelector = SubmitTypeSelector + RouteAttributeName;
|
||||
|
|
@ -106,6 +112,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
[HtmlAttributeName(AreaAttributeName)]
|
||||
public string Area { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the page.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(PageAttributeName)]
|
||||
public string Page { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URL fragment.
|
||||
/// </summary>
|
||||
|
|
@ -185,51 +197,59 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
RouteValuesPrefix,
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
url = urlHelper.Action(Action, Controller, routeValues, protocol: null, host: null, fragment: Fragment);
|
||||
}
|
||||
|
||||
output.Attributes.SetAttribute(FormAction, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ using System.ComponentModel;
|
|||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
{
|
||||
|
|
@ -16,6 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
[HtmlTargetElement("form", Attributes = ActionAttributeName)]
|
||||
[HtmlTargetElement("form", Attributes = AntiforgeryAttributeName)]
|
||||
[HtmlTargetElement("form", Attributes = AreaAttributeName)]
|
||||
[HtmlTargetElement("form", Attributes = PageAttributeName)]
|
||||
[HtmlTargetElement("form", Attributes = FragmentAttributeName)]
|
||||
[HtmlTargetElement("form", Attributes = ControllerAttributeName)]
|
||||
[HtmlTargetElement("form", Attributes = RouteAttributeName)]
|
||||
|
|
@ -26,6 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
private const string ActionAttributeName = "asp-action";
|
||||
private const string AntiforgeryAttributeName = "asp-antiforgery";
|
||||
private const string AreaAttributeName = "asp-area";
|
||||
private const string PageAttributeName = "asp-page";
|
||||
private const string FragmentAttributeName = "asp-fragment";
|
||||
private const string ControllerAttributeName = "asp-controller";
|
||||
private const string RouteAttributeName = "asp-route";
|
||||
|
|
@ -72,6 +75,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
[HtmlAttributeName(AreaAttributeName)]
|
||||
public string Area { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the page.
|
||||
/// </summary>
|
||||
[HtmlAttributeName(PageAttributeName)]
|
||||
public string Page { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the antiforgery token should be generated.
|
||||
/// </summary>
|
||||
|
|
@ -156,6 +165,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
if (Action != null ||
|
||||
Controller != null ||
|
||||
Area != null ||
|
||||
Page != null ||
|
||||
Fragment != null ||
|
||||
Route != null ||
|
||||
(_routeValues != null && _routeValues.Count > 0))
|
||||
|
|
@ -163,14 +173,15 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
// User also specified bound attributes we cannot use.
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatFormTagHelper_CannotOverrideAction(
|
||||
"<form>",
|
||||
HtmlActionAttributeName,
|
||||
"<form>",
|
||||
RouteValuesPrefix,
|
||||
ActionAttributeName,
|
||||
ControllerAttributeName,
|
||||
FragmentAttributeName,
|
||||
AreaAttributeName,
|
||||
RouteAttributeName,
|
||||
RouteValuesPrefix));
|
||||
PageAttributeName));
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
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)
|
||||
{
|
||||
// Convert from Dictionary<string, string> to Dictionary<string, object>.
|
||||
routeValues = new Dictionary<string, object>(_routeValues.Count, StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var routeValue in _routeValues)
|
||||
{
|
||||
routeValues.Add(routeValue.Key, routeValue.Value);
|
||||
}
|
||||
routeValues = new RouteValueDictionary(_routeValues);
|
||||
}
|
||||
|
||||
if (Area != null)
|
||||
{
|
||||
if (routeValues == null)
|
||||
{
|
||||
routeValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
routeValues = new RouteValueDictionary();
|
||||
}
|
||||
|
||||
// Unconditionally replace any value from asp-route-area.
|
||||
|
|
@ -202,7 +224,27 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
}
|
||||
|
||||
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(
|
||||
ViewContext,
|
||||
|
|
@ -213,42 +255,14 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
method: 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.
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatFormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified(
|
||||
"<form>",
|
||||
RouteAttributeName,
|
||||
ActionAttributeName,
|
||||
ControllerAttributeName,
|
||||
HtmlActionAttributeName,
|
||||
FragmentAttributeName));
|
||||
}
|
||||
else
|
||||
{
|
||||
tagBuilder = Generator.GenerateRouteForm(
|
||||
ViewContext,
|
||||
Route,
|
||||
routeValues,
|
||||
Fragment,
|
||||
method: null,
|
||||
htmlAttributes: null);
|
||||
output.PostContent.AppendHtml(tagBuilder.InnerHtml);
|
||||
}
|
||||
|
||||
if (tagBuilder != null)
|
||||
{
|
||||
output.MergeAttributes(tagBuilder);
|
||||
if (tagBuilder.HasInnerHtml)
|
||||
{
|
||||
output.PostContent.AppendHtml(tagBuilder.InnerHtml);
|
||||
}
|
||||
}
|
||||
|
||||
if (string.Equals(Method, "get", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
antiforgeryDefault = false;
|
||||
}
|
||||
antiforgeryDefault = !string.Equals(Method, "get", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (Antiforgery ?? antiforgeryDefault)
|
||||
|
|
|
|||
|
|
@ -11,21 +11,21 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
= new ResourceManager("Microsoft.AspNetCore.Mvc.TagHelpers.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <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>
|
||||
internal static string AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified
|
||||
internal static string CannotDetermineAttributeFor
|
||||
{
|
||||
get => GetString("AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified");
|
||||
get => GetString("CannotDetermineAttributeFor");
|
||||
}
|
||||
|
||||
/// <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>
|
||||
internal static string FormatAnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified"), p0, p1, p2, p3, p4);
|
||||
internal static string FormatCannotDetermineAttributeFor(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("CannotDetermineAttributeFor"), p0, p1);
|
||||
|
||||
/// <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>
|
||||
internal static string AnchorTagHelper_CannotOverrideHref
|
||||
{
|
||||
|
|
@ -33,13 +33,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
}
|
||||
|
||||
/// <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>
|
||||
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)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotOverrideHref"), p0, p1, p2, p3, p4, p5, p6, p7, p8, 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, p10);
|
||||
|
||||
/// <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>
|
||||
internal static string FormTagHelper_CannotOverrideAction
|
||||
{
|
||||
|
|
@ -47,10 +47,10 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
}
|
||||
|
||||
/// <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>
|
||||
internal static string FormatFormTagHelper_CannotOverrideAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotOverrideAction"), p0, p1, p2, p3, p4, p5, p6, 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, p8);
|
||||
|
||||
/// <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}'.
|
||||
|
|
@ -136,20 +136,6 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
internal static string FormatTagHelperOutput_AttributeDoesNotExist(object p0, object 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>
|
||||
/// The '{0}' property of '{1}' must not be null.
|
||||
/// </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)
|
||||
=> 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)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -117,14 +117,14 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified" 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>
|
||||
<data name="CannotDetermineAttributeFor" xml:space="preserve">
|
||||
<value>Cannot determine the '{0}' attribute for {1}. The following attributes are mutually exclusive:</value>
|
||||
</data>
|
||||
<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 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 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>
|
||||
|
|
@ -144,16 +144,10 @@
|
|||
<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}', '{3}', or '{5}' attribute.</value>
|
||||
</data>
|
||||
<data name="PropertyOfTypeCannotBeNull" xml:space="preserve">
|
||||
<value>The '{0}' property of '{1}' must not be null.</value>
|
||||
</data>
|
||||
<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>
|
||||
</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>
|
||||
|
|
@ -142,6 +142,32 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
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 />
|
||||
public virtual IHtmlContent GenerateAntiforgery(ViewContext viewContext)
|
||||
{
|
||||
|
|
@ -281,6 +307,52 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
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 />
|
||||
public TagBuilder GenerateRouteForm(
|
||||
ViewContext viewContext,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
|
|
|
|||
|
|
@ -54,6 +54,38 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
object routeValues,
|
||||
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>
|
||||
/// Generate an <input type="hidden".../> element containing an antiforgery token.
|
||||
/// </summary>
|
||||
|
|
@ -124,6 +156,35 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
string method,
|
||||
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>
|
||||
/// Generate a <form> element. The route with name <paramref name="routeName"/> generates the
|
||||
/// <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 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,
|
||||
};
|
||||
|
|
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
// Act & Assert
|
||||
await ExceptionAssert.ThrowsAsync<InvalidOperationException>(
|
||||
() => result.ExecuteResultAsync(actionContext),
|
||||
"No page named 'some-page' matches the supplied values.");
|
||||
"No page named '/some-page' matches the supplied values.");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
new ActionDescriptor());
|
||||
|
||||
var urlHelper = GetUrlHelper(actionContext, expectedUrl);
|
||||
var result = new RedirectToPageResult("MyPage", new { id = 10, test = "value" }, permanentRedirect)
|
||||
var result = new RedirectToPageResult("/MyPage", new { id = 10, test = "value" }, permanentRedirect)
|
||||
{
|
||||
UrlHelper = urlHelper,
|
||||
};
|
||||
|
|
@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
.Callback((UrlRouteContext c) => context = c)
|
||||
.Returns("some-value");
|
||||
var values = new { test = "test-value" };
|
||||
var result = new RedirectToPageResult("MyPage", values, true, "test-fragment")
|
||||
var result = new RedirectToPageResult("/MyPage", values, true, "test-fragment")
|
||||
{
|
||||
UrlHelper = urlHelper.Object,
|
||||
Protocol = "ftp",
|
||||
|
|
@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
value =>
|
||||
{
|
||||
Assert.Equal("page", value.Key);
|
||||
Assert.Equal("MyPage", value.Value);
|
||||
Assert.Equal("/MyPage", value.Value);
|
||||
});
|
||||
Assert.Equal("ftp", context.Protocol);
|
||||
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());
|
||||
}
|
||||
|
||||
[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)
|
||||
{
|
||||
var getResponse = await Client.GetAsync(request.RequestUri);
|
||||
|
|
|
|||
|
|
@ -143,6 +143,48 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
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]
|
||||
public async Task RedirectFromPage_RedirectsToPathWithoutIndexSegment()
|
||||
{
|
||||
|
|
@ -170,5 +212,18 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -366,6 +366,55 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
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]
|
||||
[InlineData("Action")]
|
||||
[InlineData("Controller")]
|
||||
|
|
@ -374,6 +423,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
[InlineData("Host")]
|
||||
[InlineData("Fragment")]
|
||||
[InlineData("asp-route-")]
|
||||
[InlineData("Page")]
|
||||
public async Task ProcessAsync_ThrowsIfHrefConflictsWithBoundAttributes(string propertyName)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -399,9 +449,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
}
|
||||
|
||||
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 " +
|
||||
"'asp-action', 'asp-controller', 'asp-area', 'asp-route', 'asp-protocol', 'asp-host', or " +
|
||||
"'asp-fragment' attribute.";
|
||||
"'href' must not have attributes starting with 'asp-route-' or an " +
|
||||
"'asp-action', 'asp-controller', 'asp-area', 'asp-route', 'asp-protocol', 'asp-host', " +
|
||||
"'asp-fragment', or 'asp-page' attribute.";
|
||||
|
||||
var context = new TagHelperContext(
|
||||
tagName: "test",
|
||||
|
|
@ -436,8 +486,88 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
"a",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||
var expectedErrorMessage = "Cannot determine an 'href' attribute for <a>. An <a> with a specified " +
|
||||
"'asp-route' must not have an 'asp-action' or 'asp-controller' attribute.";
|
||||
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_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(
|
||||
tagName: "test",
|
||||
|
|
|
|||
|
|
@ -510,8 +510,88 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
tagName,
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||
var expectedErrorMessage = $"Cannot determine a 'formaction' attribute for <{tagName}>. <{tagName}> " +
|
||||
"elements with a specified 'asp-route' must not have an 'asp-action', 'asp-controller', or 'asp-fragment' attribute.";
|
||||
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_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(
|
||||
tagName: "form-action",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
|
|
@ -201,8 +200,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
// Fixes Roslyn bug with lambdas
|
||||
generator.ToString();
|
||||
|
||||
var routeValueDictionary = (Dictionary<string, object>)routeValues;
|
||||
|
||||
var routeValueDictionary = Assert.IsType<RouteValueDictionary>(routeValues);
|
||||
Assert.Equal(2, routeValueDictionary.Count);
|
||||
var routeValue = Assert.Single(routeValueDictionary, attr => attr.Key.Equals("val"));
|
||||
Assert.Equal("hello", routeValue.Value);
|
||||
|
|
@ -552,7 +550,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
.Setup(mock => mock.GenerateRouteForm(
|
||||
viewContext,
|
||||
"Default",
|
||||
It.Is<Dictionary<string, object>>(m => string.Equals(m["name"], "value")),
|
||||
It.Is<RouteValueDictionary>(m => string.Equals(m["name"], "value")),
|
||||
null,
|
||||
null))
|
||||
.Returns(new TagBuilder("form"))
|
||||
|
|
@ -582,6 +580,50 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
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]
|
||||
[InlineData(true, "<input />")]
|
||||
[InlineData(false, "")]
|
||||
|
|
@ -638,6 +680,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
[Theory]
|
||||
[InlineData("Action")]
|
||||
[InlineData("Controller")]
|
||||
[InlineData("Page")]
|
||||
[InlineData("asp-route-")]
|
||||
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 " +
|
||||
"'action' must not have attributes starting with 'asp-route-' or an " +
|
||||
"'asp-action', 'asp-controller', 'asp-fragment', 'asp-area', or 'asp-route' attribute.";
|
||||
"'action' must not have attributes starting with 'asp-route-' or an " +
|
||||
"'asp-action', 'asp-controller', 'asp-fragment', 'asp-area', 'asp-route' or 'asp-page' attribute.";
|
||||
|
||||
var context = new TagHelperContext(
|
||||
tagName: "form",
|
||||
|
|
@ -692,8 +735,80 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
"form",
|
||||
attributes: new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
|
||||
var expectedErrorMessage = "Cannot determine an 'action' attribute for <form>. A <form> with a specified " +
|
||||
"'asp-route' must not have an 'asp-action', 'asp-controller', or 'asp-fragment' attribute.";
|
||||
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_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(
|
||||
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