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

Commit 13da6ec

Browse files
committed
throw if a WebHook action has constraints or is attribute-routed
- #248 - do not detect `[ApiController]` itself - `ApiBehaviorApplicationModelProvider` throws if that attribute used on non-attribute-routed actions nits: - update `WebHookSelectorModelProvider` methods to match constraints added - correct name of resource used in `WebHookSelectorModelProvider` to match class
1 parent 24da13d commit 13da6ec

File tree

3 files changed

+55
-74
lines changed

3 files changed

+55
-74
lines changed

src/Microsoft.AspNetCore.WebHooks.Receivers/ApplicationModels/WebHookSelectorModelProvider.cs

+42-61
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Collections.Generic;
66
using System.Globalization;
77
using System.Linq;
8-
using Microsoft.AspNetCore.Mvc.ActionConstraints;
98
using Microsoft.AspNetCore.Mvc.ApplicationModels;
109
using Microsoft.AspNetCore.Mvc.ModelBinding;
1110
using Microsoft.AspNetCore.WebHooks.Metadata;
@@ -101,29 +100,45 @@ private void Apply(ActionModel action)
101100
return;
102101
}
103102

104-
var template = ChooseTemplate();
105-
var selectors = action.Selectors;
106-
if (selectors.Count == 0)
103+
// Check for conflicting attribute routing attributes and constraints. Ignore the empty SelectorModel that
104+
// DefaultApplicationModelProvider always creates.
105+
var selectors = action.Selectors
106+
.Concat(action.Controller.Selectors)
107+
.Where(selectorModel => selectorModel.ActionConstraints.Count != 0 ||
108+
selectorModel.AttributeRouteModel != null)
109+
.ToArray();
110+
if (selectors.Length != 0)
107111
{
108-
var selector = new SelectorModel();
109-
selectors.Add(selector);
112+
// For the error message, prefer an IRouteTemplateProvider over a constraint that is an Attribute.
113+
// Prefer both over other constraints.
114+
var conflictingAttribute = selectors
115+
.Select(model => (object)model.AttributeRouteModel?.Attribute)
116+
.Concat(selectors.SelectMany(model => model.ActionConstraints.OfType<Attribute>()))
117+
.Concat(selectors.SelectMany(model => model.ActionConstraints))
118+
.FirstOrDefault();
110119

111-
AddTemplate(attribute, template, selector);
120+
var message = string.Format(
121+
CultureInfo.CurrentCulture,
122+
Resources.SelectorModelProvider_MixedRouteWithWebHookAttribute,
123+
attribute.GetType(),
124+
conflictingAttribute?.GetType(),
125+
attribute.GetType().Name);
126+
throw new InvalidOperationException(message);
112127
}
113-
else
128+
129+
// Set the template for given SelectorModel. Similar result to WebHookActionAttributeBase implementing
130+
// IRouteTemplateProvider.
131+
var selector = action.Selectors[0];
132+
selector.AttributeRouteModel = new AttributeRouteModel
114133
{
115-
for (var i = 0; i < selectors.Count; i++)
116-
{
117-
var selector = selectors[i];
118-
AddTemplate(attribute, template, selector);
119-
}
120-
}
134+
Template = ChooseTemplate(),
135+
};
121136

122137
var properties = action.Properties;
123-
AddEventMapperConstraint(properties, selectors);
124-
AddEventNamesConstraint(properties, selectors);
125-
AddIdConstraint(attribute, selectors);
126-
AddReceiverExistsConstraint(properties, selectors);
138+
AddEventNameMapperConstraint(properties, selector);
139+
AddEventNamesConstraint(properties, selector);
140+
AddIdConstraint(attribute, selector);
141+
AddReceiverNameConstraint(properties, selector);
127142
}
128143

129144
// Use a constant template since all WebHook constraints use the resulting route values and we have no
@@ -137,39 +152,7 @@ private static string ChooseTemplate()
137152
return template;
138153
}
139154

140-
// Set the template for given SelectorModel. Similar to WebHookActionAttributeBase implementing
141-
// IRouteTemplateProvider.
142-
private static void AddTemplate(WebHookAttribute attribute, string template, SelectorModel selector)
143-
{
144-
if (selector.AttributeRouteModel?.Template != null)
145-
{
146-
var message = string.Format(
147-
CultureInfo.CurrentCulture,
148-
Resources.RoutingProvider_MixedRouteWithWebHookAttribute,
149-
attribute.GetType(),
150-
selector.AttributeRouteModel.Attribute?.GetType(),
151-
attribute.GetType().Name);
152-
throw new InvalidOperationException(message);
153-
}
154-
155-
if (selector.AttributeRouteModel == null)
156-
{
157-
selector.AttributeRouteModel = new AttributeRouteModel();
158-
}
159-
160-
selector.AttributeRouteModel.Template = template;
161-
}
162-
163-
private static void AddConstraint(IActionConstraintMetadata constraint, IList<SelectorModel> selectors)
164-
{
165-
for (var i = 0; i < selectors.Count; i++)
166-
{
167-
var selector = selectors[i];
168-
selector.ActionConstraints.Add(constraint);
169-
}
170-
}
171-
172-
private void AddEventMapperConstraint(IDictionary<object, object> properties, IList<SelectorModel> selectors)
155+
private void AddEventNameMapperConstraint(IDictionary<object, object> properties, SelectorModel selector)
173156
{
174157
if (properties.TryGetValue(typeof(IWebHookEventMetadata), out var eventMetadataObject))
175158
{
@@ -183,11 +166,11 @@ private void AddEventMapperConstraint(IDictionary<object, object> properties, IL
183166
constraint = new WebHookEventNameMapperConstraint(_loggerFactory, _metadataProvider);
184167
}
185168

186-
AddConstraint(constraint, selectors);
169+
selector.ActionConstraints.Add(constraint);
187170
}
188171
}
189172

190-
private void AddEventNamesConstraint(IDictionary<object, object> properties, IList<SelectorModel> selectors)
173+
private void AddEventNamesConstraint(IDictionary<object, object> properties, SelectorModel selector)
191174
{
192175
if (properties.TryGetValue(typeof(IWebHookEventSelectorMetadata), out var eventSourceMetadata))
193176
{
@@ -196,7 +179,7 @@ private void AddEventNamesConstraint(IDictionary<object, object> properties, ILi
196179
{
197180
properties.TryGetValue(typeof(IWebHookPingRequestMetadata), out var pingRequestMetadataObject);
198181

199-
IActionConstraintMetadata constraint;
182+
WebHookEventNameConstraint constraint;
200183
if (pingRequestMetadataObject == null)
201184
{
202185
constraint = new WebHookEventNameConstraint(eventName);
@@ -210,23 +193,21 @@ private void AddEventNamesConstraint(IDictionary<object, object> properties, ILi
210193
constraint = new WebHookEventNameConstraint(eventName, _metadataProvider);
211194
}
212195

213-
AddConstraint(constraint, selectors);
196+
selector.ActionConstraints.Add(constraint);
214197
}
215198
}
216199
}
217200

218-
private void AddIdConstraint(WebHookAttribute attribute, IList<SelectorModel> selectors)
201+
private void AddIdConstraint(WebHookAttribute attribute, SelectorModel selector)
219202
{
220203
if (attribute.Id != null)
221204
{
222205
var constraint = new WebHookIdConstraint(attribute.Id);
223-
AddConstraint(constraint, selectors);
206+
selector.ActionConstraints.Add(constraint);
224207
}
225208
}
226209

227-
private void AddReceiverExistsConstraint(
228-
IDictionary<object, object> properties,
229-
IList<SelectorModel> selectors)
210+
private void AddReceiverNameConstraint(IDictionary<object, object> properties, SelectorModel selector)
230211
{
231212
var bodyTypeMetadataObject = properties[typeof(IWebHookBodyTypeMetadataService)];
232213

@@ -240,7 +221,7 @@ private void AddReceiverExistsConstraint(
240221
constraint = new WebHookReceiverNameConstraint(_metadataProvider);
241222
}
242223

243-
AddConstraint(constraint, selectors);
224+
selector.ActionConstraints.Add(constraint);
244225
}
245226
}
246227
}

src/Microsoft.AspNetCore.WebHooks.Receivers/Properties/Resources.Designer.cs

+10-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.AspNetCore.WebHooks.Receivers/Properties/Resources.resx

+3-3
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,6 @@
150150
<data name="RequestReader_ModelBindingFailed" xml:space="preserve">
151151
<value>The MVC model binding system failed. Model state is valid but model was not set.</value>
152152
</data>
153-
<data name="RoutingProvider_MixedRouteWithWebHookAttribute" xml:space="preserve">
154-
<value>'{0}' and '{1}' were applied to the same action. '{2}' must not be combined with another attribute that provides a route template.</value>
155-
</data>
156153
<data name="Security_BadSecret" xml:space="preserve">
157154
<value>Could not find a valid configuration for the '{0}' WebHook receiver, instance '{1}'. The value must be at least {2} characters long.</value>
158155
</data>
@@ -162,6 +159,9 @@
162159
<data name="Security_NoSecrets" xml:space="preserve">
163160
<value>Could not find a valid configuration for the '{0}' WebHook receiver. Configure secret keys for this receiver.</value>
164161
</data>
162+
<data name="SelectorModelProvider_MixedRouteWithWebHookAttribute" xml:space="preserve">
163+
<value>'{0}' and '{1}' were applied to the same action. '{2}' must not be combined with attribute routing or non-WebHook constraints.</value>
164+
</data>
165165
<data name="VerifyBody_NoFormData" xml:space="preserve">
166166
<value>The '{0}' WebHook receiver does not support content type '{1}'. The WebHook request must contain an entity body formatted as HTML form URL-encoded data.</value>
167167
</data>

0 commit comments

Comments
 (0)