ApiController + binding on WebHook actions = sadness
pranavkm opened this issue · comments
Consider:
[ApiController]
public class WebHookController : ControllerBase
{
[StripeWebHook]
public IActionResult Post(string id, string notificationId, string @event)
{
}
}
None of the 3-values get bound. This is because ApiController
assumes these parameters come from query (they don't exist in route) and consequently assigns a FromQuery
attribute on it. We should either
a) Make the behavior on ApiController less stringent (not set a binding source constraint on parameters)
b) Make WebHook set up the right metadata for this to work.
Let's be clear here: Were you sad or frustrated? I'd be frustrated!
BTW this issue relates to #240. WebHooks needs a few adjustments both to use some new 2.1 features e.g. ModelStateInvalidFilter
and to avoid conflicts with other new 2.1 features e.g. [ApiController]
. I like splitting the two sides of the coin: Handle the conflicts in this issue and think about using more new features in #240.
Inclinations:
throw
anInvalidOperationException
if mix of[ApiController]
and a WebHook action is detected.- Is it possible to ensure the WebHook application model changes "win" for the WebHook action. May need a new "I do not compose" flag.
- Enhance WebHook conventions to do the Right Thing:tm: when
[ApiController]
is applied. Do not require[ApiController]
but allow it.
Chose (1).
I am unable to reproduce this issue. @pranavkm's test case may be different from mine.
Or, perhaps something changed since Preview1 in this area. I tested in the dev
branch but will retest using Preview1 bits.
Expected behavior (what I see)
MVC's ApiBehaviorApplicationModelProvider
disallows use of [ApiController]
and other IApiBehaviorMetadata
attributes with actions that are not attribute routed. That provider executes before WebHookRoutingProvider
and WebHookRoutingProvider
makes the action appear to be attribute routed (and throw
s if it already is attribute routed). That is, ApiBehaviorApplicationModelProvider
should throw
when it sees a controller with [ApiController]
containing an action with any [WebHook]
attribute.
Details
The InvalidOperationException
thrown during IApplicationModelProvider
execution at startup is:
System.InvalidOperationException: Action 'GitHubCoreReceiver.Controllers.GitHubController.HandlerForItsPushes (GitHubCoreReceiver)' does not have an attribute route. Action methods on controllers annotated with ApiControllerAttribute must be attribute routed.
at Microsoft.AspNetCore.Mvc.Internal.ApiBehaviorApplicationModelProvider.EnsureActionIsAttributeRouted(Boolean controllerHasSelectorModel, ActionModel actionModel)
at Microsoft.AspNetCore.Mvc.Internal.ApiBehaviorApplicationModelProvider.OnProvidersExecuting(ApplictionModelProviderContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionDescriptorProvider.BuildModel()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionDescriptorProvider.GetDescriptors()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionDescriptorProvider.OnProvidersExecuting(ActionDeciptorProviderContext context)
at Microsoft.AspNetCore.Mvc.Internal.ActionDescriptorCollectionProvider.UpdateCollection()
at Microsoft.AspNetCore.Mvc.Internal.ActionDescriptorCollectionProvider.get_ActionDescriptors()
at Microsoft.AspNetCore.Mvc.Internal.AttributeRoute.GetTreeRouter()
at Microsoft.AspNetCore.Mvc.Internal.AttributeRoute.RouteAsync(RouteContext context)
One action (after reviewing the code)
WebHookRoutingProvider
doesn't actually throw
in all attribute routed cases, only when an attribute route on an action provides a Template
. Should check the ControllerModel
for an attribute route and check both WebHook actions and containing controllers for an HttpMethodActionConstraint
i.e. the result of an applied IActionHttpMethodProvider
(see DefaultApplicationModelProvider
).
Should probably also check for IConsumesActionConstraint
s since [Consumes]
interferes with WebHookEventNamesConstraint
s and WebHookVerifyBodyTypeFilter
s.
will retest using Preview1 bits.
See the same InvalidOperationException
on startup of Preview1 application.
Slightly odd the application doesn't exit due to the startup Exception
. It might as well crash since the same Exception
occurs when a request arrives.
@pranavkm please respond if you're able to reproduce the starting-but-not-binding behaviour.
Temporarily assigning to @pranavkm.
I want it back regardless 😈 We need to at least triage the One action ideas.
:bump:
@pranavkm please let me know how to reproduce your sadness.
@pranavkm, some boilerplate just for you 😄: Thank you for your feedback. We're closing this issue as no updates have been provided in a timely manner and we have been unable to reproduce the issue. If you have more details and are encountering this issue please add a new reply and re-open the issue.
Reopening since there's a repro. I did a dotnet new webapi
and replaced the body of ValuesController
with a GitHubWebHook
:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private ILogger<ValuesController> _logger;
public ValuesController(ILogger<ValuesController> logger)
{
_logger = logger;
}
[GitHubWebHook]
public IActionResult GitHub(string id)
{
_logger.LogCritical("WebHookId: " + id);
return Ok();
}
}
If I have ApiControllerAttribute
, the log does not have a value for id
. Removing it prints a value for the id
parameter when a webhook request is delivered.
Ah, the odd combination of [Route("api/[controller]")]
with a [WebHook]
attribute isn't checked for. Bottom line: WebHook application model providers should be as restrictive about disallowing attribute routing as ApiBehaviorApplicationModelProvider
is about requiring attribute routing.
Fixing #269 should fix this too, as long as we throw
for the worst cases.
And, thanks for the report @pranavkm!