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

Commit 4d221a1

Browse files
author
NTaylorMullen
committed
Addressed code review comments.
1 parent 9f7940c commit 4d221a1

File tree

2 files changed

+86
-85
lines changed

2 files changed

+86
-85
lines changed

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

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -55,44 +55,37 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
5555
{
5656
bool antiForgeryDefault = true;
5757

58-
var routeValues = GetRouteValues(output);
58+
var routePrefixedAttributes = output.FindPrefixedAttributes(RouteAttributePrefix);
5959

6060
// If Action contains a '/' it means the user is attempting to use the FormTagHelper as a normal form.
6161
if (Action != null && Action.Contains('/'))
6262
{
63-
if (Controller != null || routeValues != null)
63+
if (Controller != null || routePrefixedAttributes.Any())
6464
{
6565
// We don't know how to generate a form action since a Controller attribute was also provided.
6666
throw new InvalidOperationException(
6767
Resources.FormatFormTagHelper_CannotDetermineAction(
6868
"<form>",
69-
nameof(Action),
70-
nameof(Controller),
69+
nameof(Action).ToLowerInvariant(),
70+
nameof(Controller).ToLowerInvariant(),
7171
RouteAttributePrefix));
7272
}
7373

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).
74+
// User is using the FormTagHelper like a normal <form> tag, anti-forgery default should be false to
75+
// not force the anti-forgery token onto the user.
7676
antiForgeryDefault = false;
7777

7878
// Restore Action, Method and Route HTML attributes if they were provided, user wants non-TagHelper <form>.
79-
output.RestoreBoundHtmlAttribute(nameof(Action), context);
79+
output.CopyHtmlAttribute(nameof(Action), context);
8080

8181
if (Method != null)
8282
{
83-
output.RestoreBoundHtmlAttribute(nameof(Method), context);
84-
}
85-
86-
if (routeValues != null)
87-
{
88-
foreach(var routeKeys in routeValues.Keys)
89-
{
90-
output.RestoreBoundHtmlAttribute("route-" + routeKeys, context);
91-
}
83+
output.CopyHtmlAttribute(nameof(Method), context);
9284
}
9385
}
9486
else
9587
{
88+
var routeValues = GetRouteValues(output, routePrefixedAttributes);
9689
var tagBuilder = Generator.GenerateForm(ViewContext,
9790
Action,
9891
Controller,
@@ -102,15 +95,14 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
10295

10396
if (tagBuilder != null)
10497
{
105-
// We don't want to do a full merge because we want the TagHelper content to take presedence.
106-
output.MergeAttributes(tagBuilder);
98+
// We don't want to do a full merge because we want the TagHelper content to take precedence.
99+
output.Merge(tagBuilder);
107100
}
108101
}
109102

110103
if (AntiForgery ?? antiForgeryDefault)
111104
{
112105
var antiForgeryTag = Generator.GenerateAntiForgery(ViewContext);
113-
114106
if (antiForgeryTag != null)
115107
{
116108
output.Content += antiForgeryTag.ToString(TagRenderMode.SelfClosing);
@@ -119,15 +111,17 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
119111
}
120112

121113
// 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)
114+
private static Dictionary<string, object> GetRouteValues(
115+
TagHelperOutput output, IEnumerable<KeyValuePair<string, string>> routePrefixedAttributes)
123116
{
124-
var prefixedValues = output.PullPrefixedAttributes(RouteAttributePrefix);
125-
126117
Dictionary<string, object> routeValues = null;
127-
if (prefixedValues.Any())
118+
if (routePrefixedAttributes.Any())
128119
{
120+
// Prefixed values should be treated as bound attributes, remove them from the output.
121+
output.RemoveRange(routePrefixedAttributes);
122+
129123
// Generator.GenerateForm does not accept a Dictionary<string, string> for route values.
130-
routeValues = prefixedValues.ToDictionary(
124+
routeValues = routePrefixedAttributes.ToDictionary(
131125
attribute => attribute.Key.Substring(RouteAttributePrefix.Length),
132126
attribute => (object)attribute.Value);
133127
}

test/Microsoft.AspNet.Mvc.TagHelpers.Test/FormTagHelperTest.cs

Lines changed: 68 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ public async Task ProcessAsync_GeneratesExpectedOutput()
5050
},
5151
content: "Something");
5252
var urlHelper = new Mock<IUrlHelper>();
53-
var setup = urlHelper.Setup(mock =>
54-
mock.Action(It.IsAny<string>(),
55-
It.IsAny<string>(),
56-
It.IsAny<object>(),
57-
It.IsAny<string>(),
58-
It.IsAny<string>(),
59-
It.IsAny<string>()));
60-
setup.Returns("home/index");
53+
urlHelper
54+
.Setup(mock => mock.Action(It.IsAny<string>(),
55+
It.IsAny<string>(),
56+
It.IsAny<object>(),
57+
It.IsAny<string>(),
58+
It.IsAny<string>(),
59+
It.IsAny<string>()))
60+
.Returns("home/index");
6161

6262
var htmlGenerator = new TestableHtmlGenerator(metadataProvider, urlHelper.Object);
6363
var viewContext = TestableHtmlGenerator.GetViewContext(model: null,
@@ -72,6 +72,7 @@ public async Task ProcessAsync_GeneratesExpectedOutput()
7272
await formTagHelper.ProcessAsync(tagHelperContext, output);
7373

7474
// Assert
75+
Assert.Equal(3, output.Attributes.Count);
7576
var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("id"));
7677
Assert.Equal("myform", attribute.Value);
7778
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("method"));
@@ -89,7 +90,7 @@ public async Task ProcessAsync_GeneratesExpectedOutput()
8990
public async Task ProcessAsync_GeneratesAntiForgeryCorrectly(bool? antiForgery, string expectedContent)
9091
{
9192
// Arrange
92-
var expectedViewContext = CreateViewContext();
93+
var viewContext = CreateViewContext();
9394
var formTagHelper = new FormTagHelper
9495
{
9596
Action = "Index",
@@ -102,18 +103,20 @@ public async Task ProcessAsync_GeneratesAntiForgeryCorrectly(bool? antiForgery,
102103
attributes: new Dictionary<string, string>(),
103104
content: string.Empty);
104105
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
105-
generator.Setup(mock =>
106-
mock.GenerateForm(It.IsAny<ViewContext>(),
107-
It.IsAny<string>(),
108-
It.IsAny<string>(),
109-
It.IsAny<object>(),
110-
It.IsAny<string>(),
111-
It.IsAny<object>()))
112-
.Returns(new TagBuilder("form"));
113-
generator.Setup(mock => mock.GenerateAntiForgery(expectedViewContext))
106+
generator
107+
.Setup(mock => mock.GenerateForm(
108+
It.IsAny<ViewContext>(),
109+
It.IsAny<string>(),
110+
It.IsAny<string>(),
111+
It.IsAny<object>(),
112+
It.IsAny<string>(),
113+
It.IsAny<object>()))
114+
.Returns(new TagBuilder("form"));
115+
116+
generator.Setup(mock => mock.GenerateAntiForgery(viewContext))
114117
.Returns(new TagBuilder("input"));
115118

116-
SetViewContextAndGenerator(formTagHelper, expectedViewContext, generator.Object);
119+
SetViewContextAndGenerator(formTagHelper, viewContext, generator.Object);
117120

118121
// Act
119122
await formTagHelper.ProcessAsync(context, output);
@@ -125,7 +128,7 @@ public async Task ProcessAsync_GeneratesAntiForgeryCorrectly(bool? antiForgery,
125128
}
126129

127130
[Fact]
128-
public async Task ProcessAsync_UnderstandsRouteValues()
131+
public async Task ProcessAsync_BindsRouteValuesFromTagHelperOutput()
129132
{
130133
// Arrange
131134
var testViewContext = CreateViewContext();
@@ -136,39 +139,42 @@ public async Task ProcessAsync_UnderstandsRouteValues()
136139
};
137140
var context = new TagHelperContext(
138141
allAttributes: new Dictionary<string, object>());
142+
var expectedAttribute = new KeyValuePair<string, string>("ROUTEE-NotRoute", "something");
139143
var output = new TagHelperOutput(
140144
"form",
141145
attributes: new Dictionary<string, string>()
142146
{
143147
{ "route-val", "hello" },
144-
{ "roUte--Foo", "bar" },
145-
{ "ROUTEE-NotRoute", "something" }
148+
{ "roUte--Foo", "bar" }
146149
},
147150
content: string.Empty);
148-
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
149-
var setup = generator.Setup(mock =>
150-
mock.GenerateForm(It.IsAny<ViewContext>(),
151-
It.IsAny<string>(),
152-
It.IsAny<string>(),
153-
It.IsAny<object>(),
154-
It.IsAny<string>(),
155-
It.IsAny<object>()));
156-
setup.Callback<ViewContext, string, string, object, string, object>(
157-
(viewContext, actionName, controllerName, routeValues, method, htmlAttributes) =>
158-
{
159-
// Fixes Roslyn bug with lambdas
160-
generator.ToString();
151+
output.Attributes.Add(expectedAttribute);
161152

162-
var routeValueDictionary = (Dictionary<string, object>)routeValues;
163-
164-
Assert.Equal(2, routeValueDictionary.Count);
165-
var routeValue = Assert.Single(routeValueDictionary, kvp => kvp.Key.Equals("val"));
166-
Assert.Equal("hello", routeValue.Value);
167-
routeValue = Assert.Single(routeValueDictionary, kvp => kvp.Key.Equals("-Foo"));
168-
Assert.Equal("bar", routeValue.Value);
169-
});
170-
setup.Returns(new TagBuilder("form"));
171-
setup.Verifiable();
153+
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
154+
generator
155+
.Setup(mock =>mock.GenerateForm(
156+
It.IsAny<ViewContext>(),
157+
It.IsAny<string>(),
158+
It.IsAny<string>(),
159+
It.IsAny<object>(),
160+
It.IsAny<string>(),
161+
It.IsAny<object>()))
162+
.Callback<ViewContext, string, string, object, string, object>(
163+
(viewContext, actionName, controllerName, routeValues, method, htmlAttributes) =>
164+
{
165+
// Fixes Roslyn bug with lambdas
166+
generator.ToString();
167+
168+
var routeValueDictionary = (Dictionary<string, object>)routeValues;
169+
170+
Assert.Equal(2, routeValueDictionary.Count);
171+
var routeValue = Assert.Single(routeValueDictionary, kvp => kvp.Key.Equals("val"));
172+
Assert.Equal("hello", routeValue.Value);
173+
routeValue = Assert.Single(routeValueDictionary, kvp => kvp.Key.Equals("-Foo"));
174+
Assert.Equal("bar", routeValue.Value);
175+
})
176+
.Returns(new TagBuilder("form"))
177+
.Verifiable();
172178

173179
SetViewContextAndGenerator(formTagHelper, testViewContext, generator.Object);
174180

@@ -177,7 +183,7 @@ public async Task ProcessAsync_UnderstandsRouteValues()
177183

178184
Assert.Equal("form", output.TagName);
179185
var attribute = Assert.Single(output.Attributes);
180-
Assert.Equal("something", attribute.Value);
186+
Assert.Equal(expectedAttribute, attribute);
181187
Assert.Empty(output.Content);
182188
generator.Verify();
183189
}
@@ -186,7 +192,7 @@ public async Task ProcessAsync_UnderstandsRouteValues()
186192
public async Task ProcessAsync_CallsIntoGenerateFormWithExpectedParameters()
187193
{
188194
// Arrange
189-
var expectedViewContext = CreateViewContext();
195+
var viewContext = CreateViewContext();
190196
var formTagHelper = new FormTagHelper
191197
{
192198
Action = "Index",
@@ -201,13 +207,13 @@ public async Task ProcessAsync_CallsIntoGenerateFormWithExpectedParameters()
201207
attributes: new Dictionary<string, string>(),
202208
content: string.Empty);
203209
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
204-
var setup = generator.Setup(mock =>
205-
mock.GenerateForm(expectedViewContext, "Index", "Home", null, "POST", null));
206-
setup.Returns(new TagBuilder("form"));
207-
setup.Verifiable();
210+
generator
211+
.Setup(mock => mock.GenerateForm(viewContext, "Index", "Home", null, "POST", null))
212+
.Returns(new TagBuilder("form"))
213+
.Verifiable();
208214

209215
SetViewContextAndGenerator(formTagHelper,
210-
expectedViewContext,
216+
viewContext,
211217
generator.Object);
212218

213219
// Act & Assert
@@ -243,6 +249,7 @@ public async Task ProcessAsync_RestoresBoundAttributesIfActionIsURL()
243249

244250
// Assert
245251
Assert.Equal("form", output.TagName);
252+
Assert.Equal(2, output.Attributes.Count);
246253
var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("aCTiON"));
247254
Assert.Equal("http://www.contoso.com", attribute.Value);
248255
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("METhod"));
@@ -254,10 +261,10 @@ public async Task ProcessAsync_RestoresBoundAttributesIfActionIsURL()
254261
[InlineData(true, "<input />")]
255262
[InlineData(false, "")]
256263
[InlineData(null, "")]
257-
public async Task ProcessAsync_AllowsAntiForgeryIfActionIsURL(bool? antiForgery, string expectedContent)
264+
public async Task ProcessAsync_SupportsAntiForgeryIfActionIsURL(bool? antiForgery, string expectedContent)
258265
{
259266
// Arrange
260-
var expectedViewContext = CreateViewContext();
267+
var viewContext = CreateViewContext();
261268
var generator = new Mock<IHtmlGenerator>();
262269
generator.Setup(mock => mock.GenerateAntiForgery(It.IsAny<ViewContext>()))
263270
.Returns(new TagBuilder("input"));
@@ -267,7 +274,7 @@ public async Task ProcessAsync_AllowsAntiForgeryIfActionIsURL(bool? antiForgery,
267274
AntiForgery = antiForgery,
268275
};
269276
SetViewContextAndGenerator(formTagHelper,
270-
expectedViewContext,
277+
viewContext,
271278
generator.Object);
272279
var output = new TagHelperOutput("form",
273280
attributes: new Dictionary<string, string>(),
@@ -283,8 +290,8 @@ public async Task ProcessAsync_AllowsAntiForgeryIfActionIsURL(bool? antiForgery,
283290

284291
// Assert
285292
Assert.Equal("form", output.TagName);
286-
var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("aCTiON"));
287-
Assert.Equal("http://www.contoso.com", attribute.Value);
293+
var attribute = Assert.Single(output.Attributes);
294+
Assert.Equal(new KeyValuePair<string, string>("aCTiON", "http://www.contoso.com"), attribute);
288295
Assert.Equal(expectedContent, output.Content);
289296
}
290297

@@ -298,8 +305,8 @@ public async Task ProcessAsync_ThrowsIfActionIsUrlWithSpecifiedController()
298305
Controller = "Home",
299306
Method = "POST"
300307
};
301-
var expectedErrorMessage = "Cannot determine an Action for <form>. A <form> with a URL-based Action " +
302-
"must not have attributes starting with route- or a Controller attribute.";
308+
var expectedErrorMessage = "Cannot determine an action for <form>. A <form> with a URL-based action " +
309+
"must not have attributes starting with route- or a controller attribute.";
303310
var tagHelperOutput = new TagHelperOutput(
304311
"form",
305312
attributes: new Dictionary<string, string>(),
@@ -323,8 +330,8 @@ public async Task ProcessAsync_ThrowsIfActionIsUrlWithSpecifiedRoutes()
323330
Action = "http://www.contoso.com",
324331
Method = "POST"
325332
};
326-
var expectedErrorMessage = "Cannot determine an Action for <form>. A <form> with a URL-based Action " +
327-
"must not have attributes starting with route- or a Controller attribute.";
333+
var expectedErrorMessage = "Cannot determine an action for <form>. A <form> with a URL-based action " +
334+
"must not have attributes starting with route- or a controller attribute.";
328335
var tagHelperOutput = new TagHelperOutput(
329336
"form",
330337
attributes: new Dictionary<string, string>

0 commit comments

Comments
 (0)