Symfony FOSRestBundle 添加自定义 header 到响应

Symfony FOSRestBundle add custom header to response

我在 Symfony 4 中使用 FOSRestBundle 到 API 项目。我使用注释,在控制器中我有例如

use FOS\RestBundle\Controller\Annotations as Rest;

/**
 * @Rest\Get("/api/user", name="index",)
 * @param UserRepository $userRepository
 * @return array
 */
public function index(UserRepository $userRepository): array
{
return ['status' => 'OK', 'data' => ['users' => $userRepository->findAll()]];
}

config/packages/fos_rest.yaml

fos_rest:
    body_listener: true
    format_listener:
        rules:
            - { path: '^/api', priorities: ['json'], fallback_format: json, prefer_extension: false }
    param_fetcher_listener: true
    view:
        view_response_listener: 'force'
        formats:
            json: true

现在我想在我的回复中添加自定义 header 'X-Total-Found'。怎么做?

您依赖于 FOSRestBundle ViewListener,因此您的选择有限,例如无法传递自定义 header。为了实现你想要的,你需要从你的控制器调用 $this->handleView() 并传递给它一个有效的 View 实例。

您可以使用 View::create() 工厂方法或控制器 $this->view() 快捷方式。两者都将数据数组、状态代码和响应 headers 数组作为参数。然后,您可以在那里设置您的自定义 header,但您必须为每次调用都这样做。

您拥有的另一个更易于维护的选项是注册一个 on_kernel_response 事件 listener/subscriber 并以某种方式将您的自定义值 header 传递给它(您可以将其存储在例如请求属性)。

这是您的两个选择。你可能还有第三个,但我现在想不出来。


我运行进入同样的问题。我们想将分页元信息移动到 headers 并使响应不带信封(数据和元属性)。

我的环境

  • Symfony 版本 5.2
  • PHP 版本 8
  • FOS 休息包

第 1 步:创建一个 object 来保存 header 信息

    // src/Rest/ResponseHeaderBag.php
    namespace App\Rest;
    
    /**
     * Store header information generated in the controller. This same
     * object is used in the response subscriber.
     * @package App\Rest
     */
    class ResponseHeaderBag
    {
        protected array $data = [];
    
        /**
         * @return array
         */
        public function getData(): array
        {
            return $this->data;
        }
    
        /**
         * @param array $data
         * @return ResponseHeaderBag
         */
        public function setData(array $data): ResponseHeaderBag
        {
            $this->data = $data;
            return $this;
        }
    
        public function addData(string $key, $datum): ResponseHeaderBag
        {
            $this->data[$key] = $datum;
            return $this;
        }
    }

第 2 步:将 ResponseHeaderBag 注入控制器操作

 public function searchCustomers(
    ResponseHeaderBag $responseHeaderBag
): array {
   ...
   ...
   ...
   // replace magic strings and numbers with class constants and real values.
   $responseHeaderBag->add('X-Pagination-Count', 8392); 
   ...
   ...
   ...
}

第 3 步:注册订阅者并侦听响应内核事件

    // config/services.yaml        
    App\EventListener\ResponseSubscriber:
      tags:
         - kernel.event_subscriber

订阅者是监听事件的好方法。

    // src/EventListener/ResponseSubscriber
    namespace App\EventListener;
    
    use App\Rest\ResponseHeaderBag;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\HttpKernel\Event\ResponseEvent;
    use Symfony\Component\HttpKernel\KernelEvents;

    class ResponseSubscriber implements EventSubscriberInterface
    {
        public function __construct(
            protected ResponseHeaderBag $responseHeaderBag
        ){
        }

        /**
         * @inheritDoc
         */
        public static function getSubscribedEvents()
        {
            return [
                KernelEvents::RESPONSE => ['addAdditionalResponseHeaders']
            ];
        }


        /**
         * Add the response headers created elsewhere in the code.
         * @param ResponseEvent $event
         */
        public function addAdditionalResponseHeaders(ResponseEvent $event): void
        {
            $response = $event->getResponse();
            foreach ($this->responseHeaderBag->getData() as $key => $datum) {
                $response->headers->set($key, $datum);
            }
        }
    }