Added fragment to FormActionTagHelper and FormActionTagHelper

Addresses #4917
This commit is contained in:
Jaspreet Bagga 2016-10-18 09:46:50 -07:00 committed by GitHub
parent 4e1ec39a1f
commit e7c992ff06
7 changed files with 203 additions and 56 deletions

View File

@ -18,18 +18,21 @@ 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 = FragmentAttributeName)]
[HtmlTargetElement("button", Attributes = RouteAttributeName)] [HtmlTargetElement("button", Attributes = RouteAttributeName)]
[HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)] [HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)]
[HtmlTargetElement("button", Attributes = RouteValuesPrefix + "*")] [HtmlTargetElement("button", Attributes = RouteValuesPrefix + "*")]
[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 = 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)]
[HtmlTargetElement("input", Attributes = ImageRouteValuesSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageRouteValuesSelector, TagStructure = TagStructure.WithoutEndTag)]
[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 = 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)]
[HtmlTargetElement("input", Attributes = SubmitRouteValuesSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitRouteValuesSelector, TagStructure = TagStructure.WithoutEndTag)]
@ -38,6 +41,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 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";
private const string RouteValuesPrefix = "asp-route-"; private const string RouteValuesPrefix = "asp-route-";
@ -46,6 +50,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 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;
private const string ImageRouteValuesDictionarySelector = ImageTypeSelector + RouteValuesDictionaryName; private const string ImageRouteValuesDictionarySelector = ImageTypeSelector + RouteValuesDictionaryName;
@ -54,6 +59,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 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;
private const string SubmitRouteValuesDictionarySelector = SubmitTypeSelector + RouteValuesDictionaryName; private const string SubmitRouteValuesDictionarySelector = SubmitTypeSelector + RouteValuesDictionaryName;
@ -100,6 +106,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlAttributeName(AreaAttributeName)] [HtmlAttributeName(AreaAttributeName)]
public string Area { get; set; } public string Area { get; set; }
/// <summary>
/// Gets or sets the URL fragment.
/// </summary>
[HtmlAttributeName(FragmentAttributeName)]
public string Fragment { get; set; }
/// <summary> /// <summary>
/// Name of the route. /// Name of the route.
/// </summary> /// </summary>
@ -134,7 +146,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
/// <remarks>Does nothing if user provides an <c>formaction</c> attribute.</remarks> /// <remarks>Does nothing if user provides an <c>formaction</c> attribute.</remarks>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// Thrown if <c>formaction</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>, /// Thrown if <c>formaction</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>,
/// or <see cref="Route"/> are non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes. /// <see cref="Fragment"/> 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"/> /// Also thrown if <see cref="Route"/> and one or both of <see cref="Action"/> and <see cref="Controller"/>
/// are non-<c>null</c> /// are non-<c>null</c>
/// </exception> /// </exception>
@ -156,6 +168,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
if (Action != null || if (Action != null ||
Controller != null || Controller != null ||
Area != null || Area != null ||
Fragment != null ||
Route != null || Route != null ||
(_routeValues != null && _routeValues.Count > 0)) (_routeValues != null && _routeValues.Count > 0))
{ {
@ -167,6 +180,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
ActionAttributeName, ActionAttributeName,
ControllerAttributeName, ControllerAttributeName,
AreaAttributeName, AreaAttributeName,
FragmentAttributeName,
RouteAttributeName, RouteAttributeName,
RouteValuesPrefix, RouteValuesPrefix,
FormAction)); FormAction));
@ -194,7 +208,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
if (Route == null) if (Route == null)
{ {
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext); var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
var url = urlHelper.Action(Action, Controller, routeValues); var url = urlHelper.Action(Action, Controller, routeValues, protocol: null, host: null, fragment: Fragment);
output.Attributes.SetAttribute(FormAction, url); output.Attributes.SetAttribute(FormAction, url);
} }
else if (Action != null || Controller != null) else if (Action != null || Controller != null)
@ -206,12 +220,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
RouteAttributeName, RouteAttributeName,
ActionAttributeName, ActionAttributeName,
ControllerAttributeName, ControllerAttributeName,
FormAction)); FormAction,
FragmentAttributeName));
} }
else else
{ {
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext); var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
var url = urlHelper.RouteUrl(Route, routeValues); var url = urlHelper.RouteUrl(Route, routeValues, protocol: null, host: null, fragment: Fragment);
output.Attributes.SetAttribute(FormAction, url); output.Attributes.SetAttribute(FormAction, url);
} }
} }

View File

@ -16,6 +16,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 = FragmentAttributeName)]
[HtmlTargetElement("form", Attributes = ControllerAttributeName)] [HtmlTargetElement("form", Attributes = ControllerAttributeName)]
[HtmlTargetElement("form", Attributes = RouteAttributeName)] [HtmlTargetElement("form", Attributes = RouteAttributeName)]
[HtmlTargetElement("form", Attributes = RouteValuesDictionaryName)] [HtmlTargetElement("form", Attributes = RouteValuesDictionaryName)]
@ -25,6 +26,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 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";
private const string RouteValuesDictionaryName = "asp-all-route-data"; private const string RouteValuesDictionaryName = "asp-all-route-data";
@ -78,6 +80,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlAttributeName(AntiforgeryAttributeName)] [HtmlAttributeName(AntiforgeryAttributeName)]
public bool? Antiforgery { get; set; } public bool? Antiforgery { get; set; }
/// <summary>
/// Gets or sets the URL fragment.
/// </summary>
[HtmlAttributeName(FragmentAttributeName)]
public string Fragment { get; set; }
/// <summary> /// <summary>
/// Name of the route. /// Name of the route.
/// </summary> /// </summary>
@ -121,7 +129,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
/// <c>false</c>. /// <c>false</c>.
/// </remarks> /// </remarks>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// Thrown if <c>action</c> attribute is provided and <see cref="Action"/> or <see cref="Controller"/> are /// Thrown if <c>action</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/> or <see cref="Fragment"/> are
/// non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes. /// non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes.
/// </exception> /// </exception>
public override void Process(TagHelperContext context, TagHelperOutput output) public override void Process(TagHelperContext context, TagHelperOutput output)
@ -148,6 +156,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
if (Action != null || if (Action != null ||
Controller != null || Controller != null ||
Area != null || Area != null ||
Fragment != null ||
Route != null || Route != null ||
(_routeValues != null && _routeValues.Count > 0)) (_routeValues != null && _routeValues.Count > 0))
{ {
@ -158,6 +167,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
HtmlActionAttributeName, HtmlActionAttributeName,
ActionAttributeName, ActionAttributeName,
ControllerAttributeName, ControllerAttributeName,
FragmentAttributeName,
AreaAttributeName, AreaAttributeName,
RouteAttributeName, RouteAttributeName,
RouteValuesPrefix)); RouteValuesPrefix));
@ -198,6 +208,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
ViewContext, ViewContext,
Action, Action,
Controller, Controller,
Fragment,
routeValues, routeValues,
method: null, method: null,
htmlAttributes: null); htmlAttributes: null);
@ -211,7 +222,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
RouteAttributeName, RouteAttributeName,
ActionAttributeName, ActionAttributeName,
ControllerAttributeName, ControllerAttributeName,
HtmlActionAttributeName)); HtmlActionAttributeName,
FragmentAttributeName));
} }
else else
{ {
@ -219,6 +231,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
ViewContext, ViewContext,
Route, Route,
routeValues, routeValues,
Fragment,
method: null, method: null,
htmlAttributes: null); htmlAttributes: null);
} }

View File

@ -43,7 +43,7 @@ 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 '{6}' or an '{2}' or '{3}' or '{4}' or '{5}' attribute. /// 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.
/// </summary> /// </summary>
internal static string FormTagHelper_CannotOverrideAction internal static string FormTagHelper_CannotOverrideAction
{ {
@ -51,11 +51,11 @@ 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 '{6}' or an '{2}' or '{3}' or '{4}' or '{5}' attribute. /// 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.
/// </summary> /// </summary>
internal static string FormatFormTagHelper_CannotOverrideAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6) internal static string FormatFormTagHelper_CannotOverrideAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7)
{ {
return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotOverrideAction"), p0, p1, p2, p3, p4, p5, p6); return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotOverrideAction"), p0, p1, p2, p3, p4, p5, p6, p7);
} }
/// <summary> /// <summary>
@ -147,11 +147,11 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
} }
/// <summary> /// <summary>
/// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute. /// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.
/// </summary> /// </summary>
internal static string FormatFormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4) internal static string FormatFormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4, object p5)
{ {
return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified"), p0, p1, p2, p3, p4); return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified"), p0, p1, p2, p3, p4, p5);
} }
/// <summary> /// <summary>
@ -179,11 +179,11 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
} }
/// <summary> /// <summary>
/// Cannot override the '{6}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute. /// Cannot override the '{6}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{7}' must not have attributes starting with '{6}' or an '{1}', '{2}', '{3}', '{4}', or '{5}' attribute.
/// </summary> /// </summary>
internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6) internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7)
{ {
return string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6); return string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6, p7);
} }
/// <summary> /// <summary>
@ -195,11 +195,11 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
} }
/// <summary> /// <summary>
/// Cannot determine a '{4}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{1}' must not have an '{2}' or '{3}' attribute. /// Cannot determine a '{4}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.
/// </summary> /// </summary>
internal static string FormatFormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4) internal static string FormatFormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4, object p5)
{ {
return string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified"), p0, p1, p2, p3, p4); return 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)

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0 Version 2.0
The primary goals of this format is to allow a simple XML format The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes various data types are done through the TypeConverter classes
associated with the data types. associated with the data types.
Example: Example:
... ado.net/XML headers & schema ... ... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader> <resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment> <comment>This is a comment</comment>
</data> </data>
There are any number of "resheader" rows that contain simple There are any number of "resheader" rows that contain simple
name/value pairs. name/value pairs.
Each data row contains a name, and value. The row also contains a Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture. text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the Classes that don't support this are serialized and stored with the
mimetype set. mimetype set.
The mimetype is used for serialized objects, and tells the The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly: extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below. read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64 mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64 mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64 mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
@ -124,7 +124,7 @@
<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 '{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>
</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 '{6}' or an '{2}' or '{3}' or '{4}' or '{5}' attribute.</value> <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>
</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}' if '{4}' is '{5}'.</value> <value>Unexpected '{1}' expression result type '{2}' for {0}. '{1}' must be of type '{3}' if '{4}' is '{5}'.</value>
@ -142,15 +142,15 @@
<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"> <data name="FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified" xml:space="preserve">
<value>Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.</value> <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>
<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 '{6}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute.</value> <value>Cannot override the '{7}' attribute for &lt;{0}&gt;. &lt;{0}&gt; 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"> <data name="FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified" xml:space="preserve">
<value>Cannot determine a '{4}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{1}' must not have an '{2}' or '{3}' attribute.</value> <value>Cannot determine a '{4}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.</value>
</data> </data>
</root> </root>

View File

@ -0,0 +1,51 @@
// 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 Microsoft.AspNetCore.Mvc.Rendering;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
public static class DefaultHtmlGeneratorExtensions
{
public static TagBuilder GenerateForm(
this IHtmlGenerator generator,
ViewContext viewContext,
string actionName,
string controllerName,
string fragment,
object routeValues,
string method,
object htmlAttributes)
{
var tagBuilder = generator.GenerateForm(viewContext, actionName, controllerName, routeValues, method, htmlAttributes);
// Append the fragment to action
if (fragment != null)
{
tagBuilder.Attributes["action"] += "#" + fragment;
}
return tagBuilder;
}
public static TagBuilder GenerateRouteForm(
this IHtmlGenerator generator,
ViewContext viewContext,
string routeName,
object routeValues,
string fragment,
string method,
object htmlAttributes)
{
var tagBuilder = generator.GenerateRouteForm(viewContext, routeName, routeValues, method, htmlAttributes);
// Append the fragment to action
if (fragment != null)
{
tagBuilder.Attributes["action"] += "#" + fragment;
}
return tagBuilder;
}
}
}

View File

@ -221,7 +221,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{ {
Assert.Equal("delete", param.Action, StringComparer.Ordinal); Assert.Equal("delete", param.Action, StringComparer.Ordinal);
Assert.Equal("books", param.Controller, StringComparer.Ordinal); Assert.Equal("books", param.Controller, StringComparer.Ordinal);
Assert.Null(param.Fragment); Assert.Equal("test", param.Fragment, StringComparer.Ordinal);
Assert.Null(param.Host); Assert.Null(param.Host);
Assert.Null(param.Protocol); Assert.Null(param.Protocol);
Assert.Equal<KeyValuePair<string, object>>(expectedRouteValues, param.Values as RouteValueDictionary); Assert.Equal<KeyValuePair<string, object>>(expectedRouteValues, param.Values as RouteValueDictionary);
@ -238,6 +238,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{ {
Action = "delete", Action = "delete",
Controller = "books", Controller = "books",
Fragment = "test",
RouteValues = routeValues, RouteValues = routeValues,
ViewContext = viewContext, ViewContext = viewContext,
}; };
@ -277,9 +278,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
.Setup(mock => mock.RouteUrl(It.IsAny<UrlRouteContext>())) .Setup(mock => mock.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback<UrlRouteContext>(param => .Callback<UrlRouteContext>(param =>
{ {
Assert.Null(param.Fragment);
Assert.Null(param.Host); Assert.Null(param.Host);
Assert.Null(param.Protocol); Assert.Null(param.Protocol);
Assert.Equal("test", param.Fragment, StringComparer.Ordinal);
Assert.Equal("Default", param.RouteName, StringComparer.Ordinal); Assert.Equal("Default", param.RouteName, StringComparer.Ordinal);
Assert.Equal<KeyValuePair<string, object>>(expectedRouteValues, param.Values as RouteValueDictionary); Assert.Equal<KeyValuePair<string, object>>(expectedRouteValues, param.Values as RouteValueDictionary);
}) })
@ -294,6 +295,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
var tagHelper = new FormActionTagHelper(urlHelperFactory.Object) var tagHelper = new FormActionTagHelper(urlHelperFactory.Object)
{ {
Route = "Default", Route = "Default",
Fragment = "test",
RouteValues = routeValues, RouteValues = routeValues,
ViewContext = viewContext, ViewContext = viewContext,
}; };
@ -467,7 +469,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
var expectedErrorMessage = $"Cannot override the 'formaction' attribute for <{tagName}>. <{tagName}> " + var expectedErrorMessage = $"Cannot override the 'formaction' attribute for <{tagName}>. <{tagName}> " +
"elements with a specified 'formaction' must not have attributes starting with 'asp-route-' or an " + "elements with a specified 'formaction' must not have attributes starting with 'asp-route-' or an " +
"'asp-action', 'asp-controller', 'asp-area', or 'asp-route' attribute."; "'asp-action', 'asp-controller', 'asp-area', 'asp-fragment', or 'asp-route' attribute.";
var context = new TagHelperContext( var context = new TagHelperContext(
allAttributes: new TagHelperAttributeList( allAttributes: new TagHelperAttributeList(
@ -502,7 +504,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
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 = $"Cannot determine a 'formaction' attribute for <{tagName}>. <{tagName}> " +
"elements with a specified 'asp-route' must not have an 'asp-action' or 'asp-controller' attribute."; "elements with a specified 'asp-route' must not have an 'asp-action', 'asp-controller', or 'asp-fragment' attribute.";
var context = new TagHelperContext( var context = new TagHelperContext(
allAttributes: new TagHelperAttributeList( allAttributes: new TagHelperAttributeList(

View File

@ -284,6 +284,72 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
Assert.Empty(output.PostContent.GetContent()); Assert.Empty(output.PostContent.GetContent());
} }
[Fact]
public async Task ProcessAsync_AspFragmentAddsFragmentToAction()
{
// Arrange
var expectedTagName = "form";
var metadataProvider = new TestModelMetadataProvider();
var tagHelperContext = new TagHelperContext(
allAttributes: new TagHelperAttributeList
{
{ "id", "myform" },
{ "asp-route-name", "value" },
{ "asp-action", "index" },
{ "asp-controller", "home" },
{ "asp-fragment", "test" },
{ "method", "post" },
{ "asp-antiforgery", true }
},
items: new Dictionary<object, object>(),
uniqueId: "test");
var output = new TagHelperOutput(
expectedTagName,
attributes: new TagHelperAttributeList
{
{ "id", "myform" },
},
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
return Task.FromResult<TagHelperContent>(tagHelperContent);
});
var urlHelper = new Mock<IUrlHelper>();
urlHelper
.Setup(mock => mock.Action(It.IsAny<UrlActionContext>())).Returns("home/index");
var htmlGenerator = new TestableHtmlGenerator(metadataProvider, urlHelper.Object);
var viewContext = TestableHtmlGenerator.GetViewContext(
model: null,
htmlGenerator: htmlGenerator,
metadataProvider: metadataProvider);
var formTagHelper = new FormTagHelper(htmlGenerator)
{
Action = "index",
Antiforgery = true,
Controller = "home",
Fragment = "test",
ViewContext = viewContext,
RouteValues =
{
{ "name", "value" },
},
};
// Act
await formTagHelper.ProcessAsync(tagHelperContext, output);
// Assert
Assert.Equal("form", output.TagName);
Assert.Equal(TagMode.StartTagAndEndTag, output.TagMode);
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("action"));
Assert.Equal("home/index#test", attribute.Value);
}
[Fact] [Fact]
public async Task ProcessAsync_AspAreaAddsAreaToRouteValues() public async Task ProcessAsync_AspAreaAddsAreaToRouteValues()
{ {
@ -585,7 +651,7 @@ 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' or 'asp-controller' or 'asp-area' or 'asp-route' attribute."; "'asp-action', 'asp-controller', 'asp-fragment', 'asp-area', or 'asp-route' attribute.";
var context = new TagHelperContext( var context = new TagHelperContext(
allAttributes: new TagHelperAttributeList( allAttributes: new TagHelperAttributeList(
@ -616,7 +682,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
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 = "Cannot determine an 'action' attribute for <form>. A <form> with a specified " +
"'asp-route' must not have an 'asp-action' or 'asp-controller' attribute."; "'asp-route' must not have an 'asp-action', 'asp-controller', or 'asp-fragment' attribute.";
var context = new TagHelperContext( var context = new TagHelperContext(
allAttributes: new TagHelperAttributeList( allAttributes: new TagHelperAttributeList(