zendframework / zend-expressive

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

Modifications on the request's path are lost in expressive 2.2 and 3 when done in path-specific middlewares #606

Closed acelaya closed 6 years ago

acelaya commented 6 years ago

I have an expressive application where I have a middleware which is responsible of adding a prefix in the path if not present.

That middleware has to be executed only when the path starts with /rest, so, for example, if the path is /rest/users, the middleware will receive /users (since the prefix is removed), and it will return a modified request where the path is /v1.0/users. This should make next middlewares receive a request where the path is /rest/v1.0/users.

This was the behavior in expressive 2.0 and 2.1, but in expressive 2.2 and 3, this no longer works. Now, despite the uri returned by the path-specific middleware, next generic middlewares receive /rest/users.

Code to reproduce the issue

Reproducing the issue is easy.

Expressive 2.1:

Just create a new project based on the expressive skeleton v2, open the config/pipeline.php file, and just below the $app->pipe(ServerUrlMiddleware::class);, add this.

use Interop\Http\ServerMiddleware\DelegateInterface as Delegate;
use Psr\Http\Message\ServerRequestInterface as Request;
use Zend\Diactoros\Response\HtmlResponse;

// ...

$app->pipe('/rest', function (Request $request, Delegate $delegate) {
    $uri = $request->getUri();
    var_dump($uri->getPath());
    $modifiedRequest = $request->withUri($uri->withPath('/v1.0' . $uri->getPath()));
    var_dump($modifiedRequest->getUri()->getPath());
    return $delegate->process($modifiedRequest);
});
$app->pipe(function (Request $request, Delegate $delegate) {
    var_dump($request->getUri()->getPath());
    return new HtmlResponse('');
});

Then run composer serve and access http://localhost:8080/rest/users

Expected results

It should print these strings:

Actual results

That's what it does.


Expressive 3:

Again, create a new project based on the expressive skeleton v3, open the config/pipeline.php file, and just below the $app->pipe(ServerUrlMiddleware::class);, add this.

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as Handler;
use Zend\Diactoros\Response\HtmlResponse;

// ...

$app->pipe('/rest', function (Request $request, Handler $handler) {
    $uri = $request->getUri();
    var_dump($uri->getPath());
    $modifiedRequest = $request->withUri($uri->withPath('/v1.0' . $uri->getPath()));
    var_dump($modifiedRequest->getUri()->getPath());
    return $handler->handle($modifiedRequest);
});
$app->pipe(function (Request $request, Handler $handler) {
    var_dump($request->getUri()->getPath());
    return new HtmlResponse('');
});

Then run composer serve and access http://localhost:8080/rest/users

Expected results

It should print these strings too:

Actual results

This time, the result is this:

As you can see, the prefix is lost. The same happens with expressive 2.2.

I'm trying to find the reason, and in that case, I'll try to provide a PR to fix it.

acelaya commented 6 years ago

I'm almost sure the problem is in the RequestHandler which is generated on the fly inside Stratigility's PathMiddlewareDecorator, so maybe this issue should be moved there.

I'll provide a PR there.

acelaya commented 6 years ago

I have already created a PR in stratigility's repository: https://github.com/zendframework/zend-stratigility/pull/165