-
Notifications
You must be signed in to change notification settings - Fork 196
Simplify the middleware pipeline configuration #270
Simplify the middleware pipeline configuration #270
Conversation
Tests the new behavior: ```php 'middleware_pipeline' => [ [ 'middleware' => /* ... */ ], [ 'middleware' => /* ... */ ], ], ``` And also provides tests verifying that the old methodology of having `pre_` and `post_routing` keys works, but raises deprecation notices.
This patch simplifies the middleware pipeline configuration, by removing the `pre_routing` and `post_routing` keys; this also allows developers to replace the routing middleware if desired by omitting the default from the pipeline configuration. All items in the pipeline follow the same structure that was required of the `pre_` and `post_routing` keys, with one exception: the routing middleware is specified via a constant value.
Yeah 👍, that would bring us a lot of flexibility. Btw it would be great if we have to register a
I'd suggest us to document that 3rd-party modules should not register middlewares in the pipeline themselves, but let the developer (consumer) register them in proper order. |
I'm not planning on doing that. That's something you can do in custom routing solutions. |
Yeah, I think this will cause some hard to debug issues when merging config. Maybe a possible solution would be to use a specific key that would say: anything coming after is pre_routing until next token. For instance: return [
ApplicationFactory::PRE_ROUTING,
// every middleware coming after is PRE_ROUTING until next token
ApplicationFactory::ROUTING,
// every middleware comign after is ROUTING until next token
ApplicationFactory::POST_ROUTING
// every middleware comign after is POST_ROUTING until next token
ApplicationFactory::PRE_ROUTING
// If an other module's config is merged, this oculd happen... but those will still be on Pre-routing
] |
Hhmm nevermind, I think I misunderstood the purpose of this PR, it's actually to remove this distinction I suppose... |
Why not? This would simplify a lot things, completely remove the need of "route result observers" and bring a lot of flexibility to framework users. |
I both agree and disagree. On the one hand, requiring that users manually register the middleware gives greater control in your applications. On the flip side, it's not just 3rd party modules, but your own configuration. As an example, you may want middleware registered only locally, for debug purposes: debug toolbars, error handlers, etc. If you put these in a The solutions we're looking at currently:
I'll get some commits in before long. |
Because the current implementation uses the results of routing to retrieve the middleware. If we separate them into distinct middleware, the changes required include:
Not only does this result in complexity, it also requires documentation, and adds to the cognitive overhead of users trying to learn the system. Keeping all of this internal to the |
@weierophinney I'm really sorry for being very insistent :( , but I think this is actually easier for framework users and would be easy to implement as well (I can even do a PR if you want).
$request->getAttribute(RouteResult::class);
Just inject needed dependencies $pipeline->pipe($cointainer->get(MiddlewareDispatcher::class));
Actually I'd say that it makes the workflow even more easy to understand as it becomes explicit. Also this is much more clean that route result observers IMHO.
Yeah, actually it's code is simpler, but violates some SOLID principles and thus less maintainable, less replaceable, less flexible to changes... Anyway sorry, this discussion is getting out of scope for this issue, let's go back to #259 :D |
- Allows re-use for alternate routing/dispatching implementations. - Reduces immediate LOC in Application class.
I really like this approach. It really reduces the possible misunderstandings. What I like the most is that it will come within a RC6 and not with 1.1.0. Great work! |
This patch addresses zendframework#259 and comments from @danizord, separating `routeMiddleware()` into two distinct methods, `routeMiddleware()` and `dispatchMiddleware()`. This change also means we can no longer auto-register the routing middleware with the application. However, it enables new use cases, such as middleware interceptors between routing and dispatch, as well as substituting alternate routing and/or dispatch middleware in the application. Because the application no longer auto-registers the routing/dispatch middleware, the `ApplicationFactory` was updated to no longer raise an exception in situations where routes are registered but the routing middleware is not piped — since alternate routing middleware may be taking that responsibility.
oops! forgot to update the routing middleware test expectations. /me heads off to fix those |
Because routing/dispatching has been split across two separate middleware methods at this time, test setup had to change for the integration tests to ensure that the combined route/dispatch cycle is triggered.
@bakura10 We'll be doing an RC6 with these changes. |
|
I thinks features.md file need to be updated as well ( https://github.com/zendframework/zend-expressive/blob/master/doc/book/features.md ) |
-> Flow Overview diagram at features.md |
This patch deprecates route result observers for the upcoming RC6. Route result observers may still be used, but: - new `Application::routeResultObserverMiddleware()` must be piped to the application, immediately following the `routeMiddleware()`; - observers are no longer notified in the case of routing failure; - if you have switched to the new `ApplicationFactory` configuration, you will need to add the route result observer middleware to the middleware pipeline. Observers can be easily rewritten as middleware, and the migration guide demonstrates this.
@samsonasik Thanks for the heads-up; this is still a WIP, but I'd definitely missed that, and will include it in the patch. |
@harikt —
I wrote a migration guide as part of this PR. :) |
@harikt That said, I need to update zend-expressive-helpers after this is merged; I didn't want to remove the route result observer functionality until after this was accepted. |
@weierophinney I was thinking perhaps something along the below. It labels the areas correctly without the "first scan" problem of implying that each line is it's own type of middleware that comes before routing. Not a major issue of course. |
This patch updates the `UrlHelper` such that it no longer acts as a route result observer. The `UrlHelperMiddleware` was updated to look for the `RouteResult` attribute, and, if present, inject the `UrlHelper` via its `setRouteResult()` property. These changes: - are not backwards compatible, and will require a new major version. - require merging of zendframework/zend-expressive#270 for the middleware updates to work.
@ZergMark Awesome! I'll make that update shortly! |
Changes suggested by @ZergMark
@ZergMark Updated! |
@weierophinney Thanks. Looks great. |
@weierophinney it's ok for me, very nice feature! |
} | ||
|
||
if ($middleware === [$this, 'routeMiddleware'] && $this->routeMiddlewareIsRegistered) { | ||
return $this; | ||
} | ||
|
||
if ($middleware === [$this, 'dispatchMiddleware'] && $this->dispatchMiddlewareIsRegistered) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need a check if routing and dispatch middleware are registered? This adds extra logic for every call to pipe()
.
If this check is needed, I'd rather throw an exception to show the user that something is not properly configured.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's to prevent double-registration; most likely you would never want to double-route/double-dispatch, as that's expensive to do in the same request.
We can investigate making these exceptions instead, but that's out of scope for this patch; the behavior added for the dispatch middleware was done to mimic the existing behavior of registering the routing middleware.
Wow, I love this changes! 👍 👍 👍 👍 |
Per the changes in zendframework/zend-expressive#270, the route result observer system is no longer necessary, and is now deprecated.
Per the changes in zendframework/zend-expressive#270, the route result observer system is no longer necessary, and is now deprecated.
], | ||
'post_routing' => [ | ||
/* ... */ | ||
['middleware' => Your\Application\FormHelpersMiddleware::class, 'priority' => 1000], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this recipe should be updated to suggest config merging instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For standalone, priority is a good rule-of-thumb. Integrated, you'd want it likely inside a middleware pipeline definition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm talking about merging config for ViewHelperManager
instead of using middlewares to update it.
@@ -78,20 +79,22 @@ $container->set( | |||
); | |||
``` | |||
|
|||
To register the `ServerUrlMiddleware` as pre-routing pipeline middleware: | |||
To register the `ServerUrlMiddleware` as pipeline middleware following the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be before routing middleware
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please send a PR with this change, with the one from your following comment.
Need the 2.0 version going forward. This also means new configuration for the middleware pipeline. Per zendframework/zend-expressive#270, this patch updates the middleware pipeline to be an array of middleware specifications, each with a priority. It defines three specific pipelines: - "always" is registered at priority 10000, and represents a pipeline of middleware that acts as either pre-conditions or post-routing handlers (e.g., CORS support, WWW-Authentication header injection, etc.). - "routing" is registered at priority 1, and defines a middleware pipeline containing the routing, UrlHelper, and dispatch middleware; any middleware that you might want to have intercept the results of routing could go between them. - "error" is registered at priority -1000, and defines a middleware pipeline for error handlers.
Need the 2.0 version going forward. This also means new configuration for the middleware pipeline. Per zendframework/zend-expressive#270, this patch updates the middleware pipeline to be an array of middleware specifications, each with a priority. It defines three specific pipelines: - "always" is registered at priority 10000, and represents a pipeline of middleware that acts as either pre-conditions or post-routing handlers (e.g., CORS support, WWW-Authentication header injection, etc.). - "routing" is registered at priority 1, and defines a middleware pipeline containing the routing, UrlHelper, and dispatch middleware; any middleware that you might want to have intercept the results of routing could go between them. - "error" is registered at priority -1000, and defines a middleware pipeline for error handlers.
This patch simplifies a number of features of Expressive:
pre_routing
andpost_routing
keys; all middleware is piped at the same level of configuration, including routing.I've noticed one potential issue with the change: configuration merging with indexed arrays means that we cannot guarantee order. This will potentially lead to issues if the
middleware_pipeline
is defined in multiple configuration files. To address this problem, this patch introduces a new middleware specification key,priority
, which usesSplPriorityQueue
semantics, and which can be used to guarantee the order in which middleware are piped to the application.TODO
TODO post-merge:
UrlHelper
to remove theRouteResultObserverInterface
implementation, and update theUrlHelperMiddleware
to push the route result from the request, if present, into theUrlHelper
. Once complete, a new version should be tagged.RouteResult(Observer|Subject)Interface
s as deprecated.