aspnet / WebHooks

[Archived] Libraries to create and consume web hooks on ASP.NET Core. Project moved to https://github.com/aspnet/AspLabs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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:

  1. throw an InvalidOperationException if mix of [ApiController] and a WebHook action is detected.
  2. Is it possible to ensure the WebHook application model changes "win" for the WebHook action. May need a new "I do not compose" flag.
  3. 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 throws 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 IConsumesActionConstraints since [Consumes] interferes with WebHookEventNamesConstraints and WebHookVerifyBodyTypeFilters.

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!