Я разрабатываю PSR-7 (с Zend Expressive). Я выяснил метод
ServerRequestInterface::withAttribute()
и мне было интересно, почему у объекта Response нет его. Я хотел бы передать метаданные через middlewares после обработки, на стороне ответа. Есть ли способ передать «атрибуты» в Response для последующей обработки? Каков наилучший способ, следуя руководящим принципам архитектуры, достичь этого?
Спецификация PSR-7
определяет атрибуты только для запросов сервера. Они в основном используются для хранения метаданных, выведенных из входящего запроса, чтобы их можно было использовать позже, когда вы достигли своего уровня домена.
С другой стороны, ответ обычно создается на уровне домена и проходит весь пакет промежуточного программного обеспечения до фактического отправки клиенту. Таким образом, метаданные, добавленные в ответ, не имели бы места, где они могли бы быть использованы.
Я предполагаю, что если вы хотите передать данные из внутреннего промежуточного программного обеспечения в внешний, лучше всего использовать заголовки ответов.
Лучшей практикой является использование объекта запроса для передачи данных между Middleware. Ответ – это то, что выйдет клиенту, и вы хотите сохранить его в чистоте. Запрос живет только на сервере, и вы можете добавлять атрибуты (конфиденциальные данные) для прохождения. Если что-то пойдет не так, или вы ответили раньше, прежде чем удалять пользовательские данные, это не имеет значения, так как ваш ответ «чистый».
Также, если вам нужно передавать данные: Среднее ПО всегда выполняется в порядке, который он получает из конфигурации. Таким образом, вы можете убедиться, что объект запроса в MiddlewareX содержит данные, установленные MiddlewareY.
UPDATE: пример того, как передавать данные с запросом.
Middleware 2 устанавливает объект messanger, который 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 захватывает объект Messenger и обновляет его значения.
<?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); } }
Не уверен, что это «лучшая практика», но еще одна возможность – просто вставить свой объект данных в middlewares.
Middleware 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); } }
Вы могли бы даже использовать одного из посредников в качестве посланника.
Предостережение. Вы должны убедиться, что оба класса построены с одним и тем же мессенджером. Но это похоже на большинство контейнеров для инъекций зависимостей.