PSR-7 “attributes” on Response object

隐身守侯 提交于 2019-12-06 19:54:28

The PSR-7 specification defines attributes only for server requests. They are mainly use to store metadata deduced from the incoming request so that they could be used later when you reach your domain layer.

On the other hand, a response is usually created in the domain layer and traverses back all the middleware stack before being actually sent to the client. So metadata added to a response would have no place where they could actually be used.

I guess that if you want to pass data from a inner middleware to an outer one, the best way is to use response headers.

Best practise is using the request object to pass data between Middleware. The response is what is going out to the client and you want to keep this clean. The request lives only on the server and you can add (sensitive data) attributes to pass around. In case something goes wrong or you return a response early before removing the custom data, then it doesn't matter since your response is "clean".

Also if you need to pass data around: The Middleware is always executed in the order it gets from the config. This way you can make sure the request object in MiddlewareX contains the data set by MiddlewareY.

UPDATE: An example on how to pass data with the a request.

Middleware 2 sets an messanger object which Middleware 4 can use to set data which is required on the way out again.

<?php

namespace Middleware;

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

class Middleware2
{
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
    {
        $messenger = new Messenger();

        // Do something else before next middleware

        if ($next) {
            $response = $next($request->withAttribute(Messenger::class, $messenger), $response);
        }  

        // Do something with the Response after it got back
        // At this point the $messenger object contains the updated data from Middleware4

        return $response->withHeader('Content-Language', $locale);
    }
}

Middleware 4 grabs the messenger object and updates its values.

<?php

namespace Middleware;

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

class Middleware4
{
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
    {
        $messenger = $request->getAttribute(Messenger::class);
        $messenger->info('going in');
        // Do something else before next middleware

        if ($next) {
            $response = $next($request->withAttribute(FlashMessenger::class, $messenger), $response);
        }  

        // Do something with the Response after it got back
        $messenger->info('going out');

        return $response->withHeader('Content-Language', $locale);
    }
}

Not sure if this is "best practice" but another possibility is to simply inject your data object into the middlewares.

Middleware 2 has a messenger object injected and sets some data on it:

<?php

namespace Middleware;

use Interop\Http\Server\MiddlewareInterface;
use Interop\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class Middleware2
{

    private $messenger;

    public function __construct(Messenger $messenger)
    {
        $this->messenger = $messenger;
    }

    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
        ): ResponseInterface {
            $this->messenger->foo = 'bar';
            $response = $handler->handle($request);
            if ($this->messenger->foo = 'baz') {
                return $response->withHeader('Really-Important-Header', 'Baz');
            }
            return $response;
        }
}

Middleware 4 changes the data:

<?php

namespace Middleware;

use Interop\Http\Server\MiddlewareInterface;
use Interop\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class Middleware4
{

    private $messenger;

    public function __construct(Messenger $messenger)
    {
        $this->messenger = $messenger;
    }

    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
        ): ResponseInterface {
            $this->messenger->foo = 'baz';
            return $handler->handle($request);
        }
}

You might even use one of the middlewares as the messenger.

Caveat: You have to make sure that both classes get constructed with the same messenger object. But that seems to be the case with most dependency injection containers.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!