I’m using some middlewares, and I would like to pass arguments on the backleg.
Meaning: I would like the equivalent of $request->setAttribute(), e.g. $response->setAttribute() . However, such a function does not exist.
How can I send information to my outer middlewares? The only thing that is returned is the $response, and I don’t see anything that would allow this. I guess I could “hijack” the header/body to set these attributes, then unset them in the outbound middleware, but that seems hardly ideal.
Yes, you can do this with the Request object as it follows PSR-7. You are passing both the Request and Response along to the next callable. In your middleware, you would do
public function __invoke(Request $request, Response $response, $next)
{
$request = $request->withAttribute('foo', 'bar');
$response = $next($request, $response);
return $response;
Then later in your app (or in later middleware) you can retrieve it from Request again.
I can’t use $request, because I’m on my way out of the route already.
MW (middleware)
MW1 -> MW2 -> MW3 -> route (here I want to set an attribute) -> MW3 -> MW2 -> MW1 (here I want to get the attribute and do stuff with it) -> response (which should then not have the attribute anymore, neither in header nor body)
As the middlewares only ever return the $response via $next, the $request is not updated on the way back out of the route.
Upon further research, it seems this is an issue with PSR-7 itself. Just googling for “PSR7 attribute on response” yields a lot of discussion threads about this particular issue (and a number of related issues). Didn’t find a clear suggestion on how to work around it, except to (mis)use the $response->withHeader() , so I’ll stick with that for now, but I’m very interested in better ideas .
Sorry for necropost, but unfortunately this is still an unfixed “issue” that comes up every once in a while.
Since response has no way to set an attribute, my way of doing it is just passing a mutable “container class” as a request attribute, and then collecting data from it after handling the request in a middleware.
public function controllerMethod($req, $res)
{
$req->getAttribute('bag')->someAttribute = 123;
return $this->get('view')->render('blah.html');
}
// Some middleware
$app->add(function ($req, $handler) {
$bag = new stdClass;
// We're storing a mutable container class in an immutable Request,
// which kinda defeats its purpose.
$res = $handler->handle($req->withAttribute('bag', $bag));
if ($bag->someAttribute === 123) {
// do something after the request
}
return $res;
});
You can also just use the DI container to store the class, without bothering at all with request and response.
This is in no way a proper design practice! I want you to roast me and come up with something better!
The best way would obviously be to just add attributes to a response, which is unfortunately not implemented or not PSR-compliant (which one is it?):
public function controllerMethod($req, $res)
{
return $this->get('view')->render(
$res->withAttribute('someAttribute', 123), 'blah.html');
}
// Some middleware
$app->add(function ($req, $handler) {
$res = $handler->handle($req);
if ($res->getAttribute('someAttribute') === 123) {
// do something after the request
}
return $res;
});
I would argue that this assertion is incorrect, since you can set an attribute as (already) mentioned above.
Update: Only PSR-7 request interface contains methods to set attributes:
The server request provides one additional property, “attributes”, to allow consumers the ability to introspect, decompose, and match the request against application-specific rules (such as path matching, scheme matching, host matching, etc.). As such, the server request can also provide messaging between multiple request consumers.
Yes, the PSR-7 response interface contains no methods for attributes because it is designed to represent only the HTTP response message itself, without any implementation-specific metadata or contextual information.
When following good / best practices, you would fetch additional data from your “service” or from the request etc. and pass it as array or object to your use-case specific renderer, e.g. html template render or JSON renderer etc. So there is no need to use the response object for such kind of “meta” data.