PSR-7 "атрибуты" на объекте Response

Я занимаюсь разработкой с использованием PSR-7 (с Zend Expressive). Я разобрался с методом

ServerRequestInterface::withAttribute()

и мне было интересно, почему у объекта Response его нет. Я хотел бы передать метаданные через промежуточное ПО после обработки на "стороне ответа". Есть ли как-то передать "атрибуты" в Response для постобработки? Как лучше всего следовать руководству по архитектуре для достижения этой цели?

3 ответа

Рекомендуется использовать объект запроса для передачи данных между Middleware. Ответ - это то, что отправляется клиенту, и вы хотите сохранить это в чистоте. Запрос живет только на сервере, и вы можете добавить (конфиденциальные данные) атрибуты для передачи. Если что-то пойдет не так или вы вернете ответ раньше, чем удаляете пользовательские данные, это не имеет значения, поскольку ваш ответ "чистый".

Также, если вам нужно передать данные: промежуточное программное обеспечение всегда выполняется в порядке, который он получает из конфигурации. Таким образом, вы можете убедиться, что объект запроса в MiddlewareX содержит данные, установленные MiddlewareY.

ОБНОВЛЕНИЕ: пример того, как передать данные с запросом.

Middleware 2 устанавливает объект мессенджера, который Middleware 4 может использовать для установки данных, которые требуются при выходе снова.

<?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 захватывает объект мессенджера и обновляет его значения.

<?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);
    }
}

PSR-7 Спецификация определяет атрибуты только для запросов к серверу. Они в основном используются для хранения метаданных, полученных из входящего запроса, чтобы их можно было использовать позже, когда вы достигнете уровня своего домена.

С другой стороны, ответ обычно создается на уровне домена и проходит через весь стек промежуточного программного обеспечения перед его фактической отправкой клиенту. Таким образом, метаданные, добавленные в ответ, не будут иметь места, где они могут быть фактически использованы.

Я предполагаю, что если вы хотите передать данные из внутреннего промежуточного программного обеспечения во внешнее, лучшим способом является использование заголовков ответов.

Не уверен, что это "лучшая практика", но другой возможностью является просто внедрить ваш объект данных в промежуточное ПО.

Промежуточное программное обеспечение 2 содержит объект-мессенджер и устанавливает для него некоторые данные:

<?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 изменяет данные:

<?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);
        }
}

Вы можете даже использовать одно из промежуточных программ в качестве мессенджера.

Предостережение: Вы должны убедиться, что оба класса созданы с использованием одного и того же объекта мессенджера. Но это похоже на случай с большинством контейнеров для инъекций зависимости.

Другие вопросы по тегам