I’m done migrating to Slim 4 (from Slim 2 with a brief stop by Slim 3.) I got my Middleware working, but I wouldn’t say I’m happy with it.
The docs
Unclear
I don’t get it… The docs say:
Each middleware SHOULD invoke the next middleware and pass it Request and
Response objects as arguments.
Looks like a relic from Slim 3:
- Slim 3 did use
__invoke(Request $request, Response $response, callable $next)
. - Slim 4 uses
__invoke(Request $request, RequestHandler $handler)
– no
$response
.
Each middleware MUST return an instance of
Psr\Http\Message\ResponseInterface
.
But then two out of three examples that come after it use Slim\Psr7\Response
, with the third one using Psr\Http\Message\ResponseInterface
.
So which one is it, Slim\Psr7\Response
or Psr\Http\Message\ResponseInterface
?
Bad example
The ExampleBeforeMiddleware example will break any redirect. Because it creates a new Response and only copies the old body, any redirect set further down the middlewares layers will be lost.
One way to fix it would be to replace the body of a response instead of creating a brand new Response. But how to do it? I understand we’re supposed to rely on StreamInterface but couldn’t find an example.
Another way would be to create a new Response, but copy the headers. Is there a one-liner to copy the headers, like there is for copying the body (with (string) $response->getBody()
)?
Accessing response in the Middleware
In v3, accessing the response before accessing the next middleware layer was
straightforward and the logic easy to read. e.g.:
public function __invoke(Request $request, Response $response, callable $next)
{
/**
* Do stuff BEFORE passing to the next Middleware layer.
* Example here is part of a Login middleware.
* In this case, `before` checks for credentials and set cookies and redirect
* as needed.
*/
$response = $this->before($request, $response);
/**
* Invoke the next Middleware layer.
* "Each middleware SHOULD invoke the next middleware and pass it
* Request and Response objects as arguments."
*
* Don't run it for redirects as this is not needed (we're redirecting!)
* and would break the app.
*/
if (! $response->isRedirect()) {
$response = $next($request, $response);
}
/**
* Do stuff AFTER the route is rendered.
* e.g. `$response->getBody()->write('AFTER');`
* e.g. `$response = $this->after($request, $response);` for symmetry.
*/
// Nothing to do for now.
/**
* Finally, return the response.
* "a middleware MUST return an instance of \Psr\Http\Message\ResponseInterface"
*/
return $response;
}
In v4, I’m confused… How to get the response BEFORE running the next level of
middleware? Seems I have to create a new response all the time. So this makes me
wonder: in v3, was the $response at the beginning of _invoke
actually always
empty too?
Thanks for the clarifications,
Fabien