2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
4
using System ;
5
+ using System . Collections . Generic ;
5
6
using System . Linq ;
6
7
using Microsoft . AspNet . Mvc . Rendering ;
7
8
using Microsoft . AspNet . Razor . Runtime . TagHelpers ;
@@ -16,7 +17,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
16
17
public class FormTagHelper : TagHelper
17
18
{
18
19
private const string RouteAttributePrefix = "route-" ;
19
- private static readonly bool DefaultAntiForgeryBehavior = true ;
20
20
21
21
[ Activate ]
22
22
private ViewContext ViewContext { get ; set ; }
@@ -43,7 +43,8 @@ public class FormTagHelper : TagHelper
43
43
public string Method { get ; set ; }
44
44
45
45
/// <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.
47
48
/// </summary>
48
49
[ HtmlAttributeName ( "anti-forgery" ) ]
49
50
public bool ? AntiForgery { get ; set ; }
@@ -52,64 +53,86 @@ public class FormTagHelper : TagHelper
52
53
/// <remarks>Does nothing if <see cref="Action"/> contains a '/'.</remarks>
53
54
public override void Process ( TagHelperContext context , TagHelperOutput output )
54
55
{
56
+ bool antiForgeryDefault = true ;
57
+
58
+ var routeValues = GetRouteValues ( output ) ;
59
+
55
60
// If Action contains a '/' it means the user is attempting to use the FormTagHelper as a normal form.
56
61
if ( Action != null && Action . Contains ( '/' ) )
57
62
{
58
- if ( Controller != null )
63
+ if ( Controller != null || routeValues != null )
59
64
{
60
65
// We don't know how to generate a form action since a Controller attribute was also provided.
61
66
throw new InvalidOperationException (
62
67
Resources . FormatFormTagHelper_CannotDetermineAction (
63
- nameof ( FormTagHelper ) ,
68
+ "<form>" ,
64
69
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 ) ;
66
84
}
67
85
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
+ }
69
93
}
70
94
else
71
95
{
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
-
79
96
var tagBuilder = Generator . GenerateForm ( ViewContext ,
80
97
Action ,
81
98
Controller ,
82
99
routeValues ,
83
100
Method ,
84
101
htmlAttributes : null ) ;
85
102
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
+ }
87
109
88
- if ( AntiForgery ?? DefaultAntiForgeryBehavior )
110
+ if ( AntiForgery ?? antiForgeryDefault )
111
+ {
112
+ var antiForgeryTag = Generator . GenerateAntiForgery ( ViewContext ) ;
113
+
114
+ if ( antiForgeryTag != null )
89
115
{
90
- var antiForgeryTag = Generator . GenerateAntiForgery ( ViewContext ) ;
91
116
output . Content += antiForgeryTag . ToString ( TagRenderMode . SelfClosing ) ;
92
117
}
93
118
}
94
119
}
95
120
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 )
98
123
{
99
- if ( Action != null )
100
- {
101
- output . RestoreBoundHtmlAttribute ( nameof ( Action ) , context ) ;
102
- }
124
+ var prefixedValues = output . PullPrefixedAttributes ( RouteAttributePrefix ) ;
103
125
104
- if ( Controller != null )
126
+ Dictionary < string , object > routeValues = null ;
127
+ if ( prefixedValues . Any ( ) )
105
128
{
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 ) ;
107
133
}
108
134
109
- if ( Method != null )
110
- {
111
- output . RestoreBoundHtmlAttribute ( nameof ( Method ) , context ) ;
112
- }
135
+ return routeValues ;
113
136
}
114
137
}
115
138
}
0 commit comments