zendframework / zend-expressive

PSR-15 middleware in minutes!
BSD 3-Clause "New" or "Revised" License
711 stars 197 forks source link

Allow to short-circuit through result observer #247

Closed bakura10 closed 8 years ago

bakura10 commented 8 years ago

Hi,

I have a use case where I'd like a result observer to be able to "halt" the dispatch process (basically, I have a pre-routing middleware doing some work, but this work can then only be exploited once we have matched a route... but I'd like this check to prevent BEFORE dispatching to the route middleware to avoid potentially costly work).

I'm wondering if just throwing an exception in the observer is enough to stop the propagation? I'm having a hard actually finding where those exceptions are catched in Expressive codebase, and delegated to the ErrorHandler. I've looked for a try {} block anywhere in Expressive and Stratigility, but couldn't find.

RalfEggert commented 8 years ago

I have the feeling that this is somehow connected with my issue #249

I also want to stop the normal dispatch process within a result observer to show a login or a forbidden page or to do a redirection then. I will play around with your idea of throwing an exception now.

bakura10 commented 8 years ago

I've actually found an alternative solution by injecting the Router to a pre_routing middleware, and doing a pre-match that allowed me to do additional authorization checks BEFORE going to the costly middleware (I suspect the ->match being negligeable compared to various DB calls that could occur in a middleware).

I've explained in more details in your thread :).

RalfEggert commented 8 years ago

Thanks

weierophinney commented 8 years ago

There's another way to do this: any "middleware" defined by a route can also be an array of middleware:

[
    'name' => 'guarded-middleware',
    'path' => '/api/resource',
    'middleware' => [
        AuthorizationMiddleware::class,
        ResourceMiddleware::class,
    ],
    'allowed_methods' => ['GET', 'POST', 'PATCH', 'DELETE'],
],

This approach allows you to have routes that require specific middleware workflows, and this will likely be how we approach Apigility 2.0 (though we'll likely have a middleware pipe that aggregates the various discrete middlewares).

Regarding this statement:

I've looked for a try {} block anywhere in Expressive and Stratigility, but couldn't find.

It's inside Zend\Stratigility\Dispatch: https://github.com/zendframework/zend-stratigility/blob/master/src/Dispatch.php#L75-L85

Essentially, raising an exception from any middleware is definitely reasonable. Another possibility is calling $next() with a third argument, which, when provided, is intended to signify an error, and causes Stratigility to start iterating error middleware to resolve the problem and/or return an error response. Since the route result observers are triggered within middleware, raising an exception from an observer would definitely work.

weierophinney commented 8 years ago

@bakura10 There's a new solution as of RC6. That version obsoletes the observer approach; instead, you place middleware between the routing and dispatch middleware, which allows you to short-circuit.