-
Notifications
You must be signed in to change notification settings - Fork 196
Requests with an unmatched route do not contain the RouteResult #344
Comments
@nesl247 I think the logic here was that an unmatched route would be handled by a middleware further down the stack. The issue (and I have the same one) is if any middleware calls something like below BEFORE the 404 handler is called :
On a failed route match, this returns a @weierophinney Can you further explain the logic of letting non-routable URLs pass through the routing and dispatching? It is necessary to just have a 404-handling middleware right after Here is a use case where you would need a middleware-pipeline.global.php
If we only want to run authentication checks on certain routes,
I think @nesl247 and I were expecting that |
To me it sounds pretty logical. However an exception should be thrown by the FinalHandler if the middleware stack is exhausted, and no middleware has returned a response. The logic in the final handler should kick in and recover gracefully by returning a 404 response since no middleware was able to handle the request. What I do is check for the RouteResult where needed and if there is none the middleware returns early. Basically the same as I think one of the reasons that there is no Exception thrown if there is no route might be that you can have a sort of "catch all route". e.g. In case no route matches, it checks the database if a document matches the current path. If an exception would be thrown you can't have that after the main routes are checked. You can always add middleware that checks for the RouteResult and throws an exception, right after Also I think submitting a PR might be useless because it would mean a BC break. Changes have been made already to the Stratigility develop branch which changes the error handler completely (The FinalHandler is removed). I'm not sure about the timeframe but eventually it should make its way into the next version of Expressive. |
Why is it logical that there is no RouteResult, especially when it has a method to check if it's a failure or not? It's a RouteResult, not a RouteMatch after all. |
Like I said, if there is no route, I don't expect a RouteResult. I guess it's something personal. Why there are isSuccess() and isFailure() methods? AFAIK they were only used by the route result observers, which are deprecated. The RouteResult wasn't available publicly since the beginning, it is injected into the Request since version 1.0.0.rc3. |
I agree that there is a logic to not returning a Either way, it would be nice if the documentation reflected the fact that any middleware put between |
@xtreamwayz Have you by chance tested out the Strategility |
I don't know what the "official" position is but taken from that PR:
I would interpret that as it only injects the RouteResult if a route is matched and if there is no route nothing is injected into the Request object. For an "official" statement you need to wait for @weierophinney to reply. I agree that the documentation should explain this a lot better. Probably the whole chapter about Route Result Observers can be replaced with more info about the RouteResult.
I've been playing with it but it doesn't work yet. So far only the changes to Stratigility have been made and some Expressive parts need to be rewritten first. However you can sort of mimic the new error handling if you don't setup Expressive via configuration: https://github.com/weierophinney/mwop.net/blob/master/public/index.php#L27-L35 |
@nesl247 —
I disagree with this. It's entirely possible to add middleware that executes
I like this idea. HOWEVER... It'd be a breaking change at this point. The reason is thus: currently, if if (! ($result = $request->getAttribute(RouteResult::class, false))) {
return $next($request, $response);
} If we always inject, that now has to become: $result = $request->getAttribute(RouteResult::class, false);
if (! $result || $result->isFailure()) {
return $next($request, $response);
} If that change is not made, the code will continue to work, but it could lead to
Dispatch must occur after routing, but dispatch only works if a route result If the "not found" middleware were to be between routing and dispatch, what Based on the above observations, my plan will be to add documentation indicating if (! ($result = $request->getAttribute(RouteResult::class, false))) {
return $next($request, $response);
} |
Great! The docs looked great in the other commit.
@weierophinney I can see how this should be the default, but is there anything wrong with throwing an Exception to be caught by
I'm doing this because I have four middleware between routing and dispatch that depend on the route being set. I would rather |
@moderndeveloperllc once we have #396 and 1.1 in place, that'll be an excellent approach. Until then, doing so triggers either error middleware or the final handler, both of which are being deprecated. |
@weierophinney Thanks. I'm already doing this as I've moved to the http-interop style of doing middleware (via the |
I found this bug when I was tracking down why an internal application was throwing an error in one of my post-route-matched middleware to configure NewRelic. To my surprise, a route that is not matched does not actually throw an exception.
I have two opinions on how to fix it.
The first option is that an exception should be thrown in
Application::routeMiddleware()
(https://github.com/zendframework/zend-expressive/blob/master/src/Application.php#L405) because the rest of the application is kinda done at this point. This is the option that I prefer becauseApplication::dispatchMiddleware()
(https://github.com/zendframework/zend-expressive/blob/master/src/Application.php#L446) already depends upon aRouteResult
, thus one would likely have to have two routers both supported expressive in order for the second option to really make sense.The other option is that this isn't done because technically the pipeline is still there, and anyone can have other middleware to handle a route. While this might be the case for some people, it seems really odd to have two routers that both would inject a
RouteResult
into the request.If an exception is not the desired way to fix this, then we should really always inject the
RouteResult
into the request, and changedispatchMiddleware()
to checkisFailure()
rather than just assuming the result being in the request means it was a success. The justification for this is that it is calledRouteResult
and even has asuccess
property, indicating it does not guarantee a success.The other benefit to use an exception is that all post-route-matched middleware in the pipeline don't have to make a check for whether or not the route was successful or not, as they probably shouldn't be executed anyways. If the exception isn't to be used, the comment in the
middleware-pipeline.global.php
should probably state that you should probably call theisFailure()
method and and if it is true,return $next($request, $response)
or throw an exception (if that's the intended behavior).I would have submitted a PR for this, but since there are two ways to fix it, it's not worth submitting one if it won't be used due to the other method being preferred. Once discussed I can submit one.
The text was updated successfully, but these errors were encountered: