Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit 8b55118

Browse files
author
NTaylorMullen
committed
Add tests for FormTagHelper
- Modified FormTagHelper to utilize the AntiForgeryToken when the Action is URL based (different behavior).
1 parent 28b447c commit 8b55118

File tree

4 files changed

+435
-33
lines changed

4 files changed

+435
-33
lines changed

src/Microsoft.AspNet.Mvc.TagHelpers/FormTagHelper.cs

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.Linq;
67
using Microsoft.AspNet.Mvc.Rendering;
78
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
@@ -16,7 +17,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
1617
public class FormTagHelper : TagHelper
1718
{
1819
private const string RouteAttributePrefix = "route-";
19-
private static readonly bool DefaultAntiForgeryBehavior = true;
2020

2121
[Activate]
2222
private ViewContext ViewContext { get; set; }
@@ -43,7 +43,8 @@ public class FormTagHelper : TagHelper
4343
public string Method { get; set; }
4444

4545
/// <summary>
46-
/// Whether the anti-forgery token should be generated. Defaults to <c>true</c>.
46+
/// Whether the anti-forgery token should be generated. Defaults to <c>true</c> if <see cref="Action"/> is not
47+
/// a URL, <c>false</c> otherwise.
4748
/// </summary>
4849
[HtmlAttributeName("anti-forgery")]
4950
public bool? AntiForgery { get; set; }
@@ -52,64 +53,86 @@ public class FormTagHelper : TagHelper
5253
/// <remarks>Does nothing if <see cref="Action"/> contains a '/'.</remarks>
5354
public override void Process(TagHelperContext context, TagHelperOutput output)
5455
{
56+
bool antiForgeryDefault = true;
57+
58+
var routeValues = GetRouteValues(output);
59+
5560
// If Action contains a '/' it means the user is attempting to use the FormTagHelper as a normal form.
5661
if (Action != null && Action.Contains('/'))
5762
{
58-
if (Controller != null)
63+
if (Controller != null || routeValues != null)
5964
{
6065
// We don't know how to generate a form action since a Controller attribute was also provided.
6166
throw new InvalidOperationException(
6267
Resources.FormatFormTagHelper_CannotDetermineAction(
63-
nameof(FormTagHelper),
68+
"<form>",
6469
nameof(Action),
65-
nameof(Controller)));
70+
nameof(Controller),
71+
RouteAttributePrefix));
72+
}
73+
74+
// If the anti-forgery token was not specified then don't assume that it's on (user is using the
75+
// FormTagHelper like a normal <form> tag).
76+
antiForgeryDefault = false;
77+
78+
// Restore Action, Method and Route HTML attributes if they were provided, user wants non-TagHelper <form>.
79+
output.RestoreBoundHtmlAttribute(nameof(Action), context);
80+
81+
if (Method != null)
82+
{
83+
output.RestoreBoundHtmlAttribute(nameof(Method), context);
6684
}
6785

68-
RestoreBoundHtmlAttributes(context, output);
86+
if (routeValues != null)
87+
{
88+
foreach(var routeKeys in routeValues.Keys)
89+
{
90+
output.RestoreBoundHtmlAttribute("route-" + routeKeys, context);
91+
}
92+
}
6993
}
7094
else
7195
{
72-
var prefixedValues = output.PullPrefixedAttributes(RouteAttributePrefix);
73-
74-
// Generator.GenerateForm does not accept a Dictionary<string, string> for route values.
75-
var routeValues = prefixedValues.ToDictionary(
76-
attribute => attribute.Key.Substring(RouteAttributePrefix.Length),
77-
attribute => (object)attribute.Value);
78-
7996
var tagBuilder = Generator.GenerateForm(ViewContext,
8097
Action,
8198
Controller,
8299
routeValues,
83100
Method,
84101
htmlAttributes: null);
85102

86-
output.Merge(tagBuilder);
103+
if (tagBuilder != null)
104+
{
105+
// We don't want to do a full merge because we want the TagHelper content to take presedence.
106+
output.MergeAttributes(tagBuilder);
107+
}
108+
}
87109

88-
if (AntiForgery ?? DefaultAntiForgeryBehavior)
110+
if (AntiForgery ?? antiForgeryDefault)
111+
{
112+
var antiForgeryTag = Generator.GenerateAntiForgery(ViewContext);
113+
114+
if (antiForgeryTag != null)
89115
{
90-
var antiForgeryTag = Generator.GenerateAntiForgery(ViewContext);
91116
output.Content += antiForgeryTag.ToString(TagRenderMode.SelfClosing);
92117
}
93118
}
94119
}
95120

96-
// Restores bound HTML attributes when we detect that a user is using the AnchorTagHelper as a normal <a> tag.
97-
private void RestoreBoundHtmlAttributes(TagHelperContext context, TagHelperOutput output)
121+
// TODO: We will not need this method once https://github.com/aspnet/Razor/issues/89 is completed.
122+
private static Dictionary<string, object> GetRouteValues(TagHelperOutput output)
98123
{
99-
if (Action != null)
100-
{
101-
output.RestoreBoundHtmlAttribute(nameof(Action), context);
102-
}
124+
var prefixedValues = output.PullPrefixedAttributes(RouteAttributePrefix);
103125

104-
if (Controller != null)
126+
Dictionary<string, object> routeValues = null;
127+
if (prefixedValues.Any())
105128
{
106-
output.RestoreBoundHtmlAttribute(nameof(Controller), context);
129+
// Generator.GenerateForm does not accept a Dictionary<string, string> for route values.
130+
routeValues = prefixedValues.ToDictionary(
131+
attribute => attribute.Key.Substring(RouteAttributePrefix.Length),
132+
attribute => (object)attribute.Value);
107133
}
108134

109-
if (Method != null)
110-
{
111-
output.RestoreBoundHtmlAttribute(nameof(Method), context);
112-
}
135+
return routeValues;
113136
}
114137
}
115138
}

src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,6 @@
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120120
<data name="FormTagHelper_CannotDetermineAction" xml:space="preserve">
121-
<value>Cannot determine an {1} for {0}. A {0} with a speicified URL based {1} must not have a {2} attribute.</value>
121+
<value>Cannot determine an {1} for {0}. A {0} with a URL-based {1} must not have attributes starting with {3} or a {2} attribute.</value>
122122
</data>
123123
</root>

0 commit comments

Comments
 (0)