lcobucci/content-negotiation-middleware

一个用于处理内容协商的PSR-15中间件

3.2.0 2023-11-22 21:33 UTC

README

Total Downloads Latest Stable Version Unstable Version

Build Status Code Coverage

动机

类似于 middlewares/negotiation 的包在根据 Accept 头(或 URI 中的扩展名)检测正确的内容类型方面做得非常好,然而它们将格式化内容的任务委托给了 RequestHandler

这对于大多数情况都很好,但在复杂软件中通常会产生大量的重复,每个 RequestHandler 都应该执行这种格式化(或依赖于某个组件来完成)。这种逻辑也应该添加到处理异常并将其转换为适当的 HTTP 响应的中间件中。

本中间件的目标是提供完整的内容协商(检测和格式化)。

安装

此包可在Packagist上找到,我们建议您使用Composer进行安装

composer require lcobucci/content-negotiation-middleware middlewares/negotiation laminas/laminas-diactoros

冒险模式

如果您已经准备好冒险,并且不想使用 middlewares/negotiation 来处理检测或 laminas/diactoros 来创建响应体(StreamInterface 实现),请不要绝望!您只需使用 ContentTypeMiddleware::__construct() 而不是 ContentTypeMiddleware::fromRecommendedSettings() 即可。

我们对这些包有一些偏好,不想重新发明轮子...但毕竟,这是一个自由的世界。

PHP 配置

为了确保其他组件返回预期的对象,我们决定使用 assert(),这是 PHP 中一个非常有趣的功能,但使用得不多。关于 assert() 的好处是,我们可以(并且应该)在生产模式中禁用它,这样我们就不会有不必要的语句。

因此,对于生产模式,我们建议您在您的 php.ini 中将 zend.assertions 设置为 -1。对于开发,您应该将 zend.assertions 留为 1 并将 assert.exception 设置为 1,这将使 PHP 在出错时抛出 AssertionError

有关更多信息,请参阅文档:https://secure.php.net/manual/en/function.assert.php

使用方法

您首先要做的是使用正确的配置创建中间件

<?php
declare(strict_types=1);

use Lcobucci\ContentNegotiation\ContentTypeMiddleware;
use Lcobucci\ContentNegotiation\Formatter\Json;
use Lcobucci\ContentNegotiation\Formatter\StringCast;
use Laminas\Diactoros\StreamFactory;

$middleware = ContentTypeMiddleware::fromRecommendedSettings(
    // First argument is the list of formats you want to support:
    [
        'json',
        // You may also specify the full configuration of the format.
        // That's handy if you need to add extensions or mime-types:
        'html' => [
            'extension' => ['html', 'htm', 'php'],
            'mime-type' => ['text/html', 'application/xhtml+xml'],
            'charset' => true,
        ],
    ],
    // It's very important to mention that:
    //
    // * the first format will be used as fallback (no acceptable mime type
    // found)
    // * the order of elements does matter
    // * the first element of `mime-type` list will be used as negotiated type


    // The second argument is the list of formatters that will be used for
    // each mime type:
    [
        'application/json' => new Json(),
        'text/html'        => new StringCast(),
    ],

     // The last argument is any implementation for the StreamFactoryInterface (PSR-17)  
    new StreamFactory()
);

然后您必须将中间件添加到管道的非常开始处,这取决于您使用的库/框架,但大致如下

<?php

// ...

$application->pipe($middleware);

最后,您只需使用您创建的请求处理程序的返回值 UnformattedResponse 来触发所需的格式化

<?php
declare(strict_types=1);

namespace Me\MyApp;

use Fig\Http\Message\StatusCodeInterface;
use Lcobucci\ContentNegotiation\UnformattedResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Laminas\Diactoros\Response;

final class MyHandler implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        // Does the necessary process and creates `$result` with the unformatted
        // content.

        return new UnformattedResponse(
            (new Response())->withStatus(StatusCodeInterface::STATUS_CREATED),
            $result
        );
    }
}

格式化器

我们默认提供了一些基本的格式化器

如果您想创建自定义格式化器,需要实现Formatter接口

<?php
declare(strict_types=1);

namespace Me\MyApp;

use Lcobucci\ContentNegotiation\Formatter;
use Lcobucci\ContentNegotiation\UnformattedResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;

final class MyFancyFormatter implements Formatter
{
    public function format(UnformattedResponse $response, StreamFactoryInterface $streamFactory): ResponseInterface
    {
        $content = ''; // Do some fancy formatting of $response->getUnformattedContent() and put into $content

        return $response->withBody($streamFactory->createStream($content));
    }
}

许可证

MIT,见LICENSE