rnd-cosoft/api-tools-content-negotiation

Laminas 模块,提供内容协商功能


README

Build Status

🇷🇺 俄罗斯公民

我们是来自不同国家的 Laminas 参与者。我们中许多人都有俄罗斯和乌克兰的朋友、亲属和同事。我们中的一些人出生在俄罗斯。我们中的一些人现在住在俄罗斯。我们中的一些祖父母在二战中与法西斯作战。这里没有人支持法西斯主义。

我们中的一位成员有一位乌克兰亲属,她和儿子一起逃离了家园。火车因前方道路上的轰炸而延误。我们有朋友躲在防空洞里。我们在空袭后焦急地关注他们的消息,空袭不分青红皂白地袭击医院、学校、幼儿园和住宅。我们不是从任何媒体上得知这些。这是我们亲身体验到的。

您足够信任我们,愿意使用我们的程序,我们请求您再次信任我们。我们需要帮助。走出家门,抗议这场毫无意义的战争。停止杀戮。说“停止战争!”

🇺🇸 致俄罗斯公民

我们 Laminas 团队来自世界各地。我们中许多人都有俄罗斯和乌克兰的朋友、亲属和同事。我们中的一些人出生在俄罗斯。我们中的一些人现在住在俄罗斯。我们中的一些祖父母在二战中与纳粹作战。这里没有人支持法西斯主义。

我们团队中的一位成员有一位乌克兰亲属,她和儿子一起逃离了家园。火车因前方道路上的轰炸而延误。我们有朋友躲在防空洞里。我们在空袭后焦急地关注他们的消息,空袭不分青红皂白地袭击医院、学校、幼儿园和住宅。我们不是从任何媒体上得知这些。这是我们亲身体验到的。

您足够信任我们,愿意使用我们的软件。我们请求您信任我们,说出真相。我们需要您的帮助。走出家门,抗议这场毫无必要的战争。停止杀戮。说“停止战争!”

简介

api-tools-content-negotiation 是一个模块,用于自动化 Laminas 框架应用程序中的内容协商任务。

该模块提供以下功能

  • Accept 头媒体类型映射到特定的视图模型类型,并自动将控制器结果转换为这些视图模型类型。
  • 定义 Accept 头媒体类型白名单;具有不在白名单中的 Accept 媒体类型的请求将被立即拒绝,并返回 406 Not Acceptable 响应。
  • 定义 Content-Type 头媒体类型白名单;发送不在白名单中的 Content-Type 媒体类型的内容体的请求将被立即拒绝,并返回 415 Unsupported Media Type 响应。

要求

请参阅 composer.json 文件。

安装

运行以下 composer 命令

$ composer require laminas-api-tools/api-tools-content-negotiation

或者,手动将以下内容添加到您的 composer.json 文件的 require 部分

"require": {
    "laminas-api-tools/api-tools-content-negotiation": "^1.2"
}

然后运行 composer update 以确保模块已安装。

最后,将模块名称添加到您的项目 config/application.config.php 文件中的 modules 键下

return [
    /* ... */
    'modules' => [
        /* ... */
        'Laminas\ApiTools\ContentNegotiation',
    ],
    /* ... */
];

laminas-component-installer

如果您使用 laminas-component-installer,该插件会为您安装 api-tools-content-negotiation 作为模块。

配置

用户配置

此模块用户配置的最高级配置键是 api-tools-content-negotiation

键: controllers

使用 controllers 键将内容协商策略映射到特定的控制器服务名称(从顶级 controllers 部分)。控制器数组的价值部分可以是 命名选择器(见下文 selectors),或选择器定义。

选择器定义由一个数组组成,其中数组的键是视图模型名称,值是一个索引数组,当匹配时将选择该视图模型。

示例

'controllers' => [
    // Named selector:
    'Application\Controller\HelloWorld1' => 'Json',

    // Selector definition:
    'Application\Controller\HelloWorld2' => [
        'Laminas\ApiTools\ContentNegotiation\JsonModel' => [
            'application/json',
            'application/*+json',
        ],
    ],
],

键:selectors

selectors 键用于创建用于在不同控制器之间重用的命名选择器定义。选择器数组的键将是一个用于关联选择器定义(使用在 controllers 键中描述的格式)的名称。

示例

'selectors' => [
    'Json' => [
        'Laminas\ApiTools\ContentNegotiation\JsonModel' => [
            'application/json',
            'application/*+json',
        ],
    ],
],

选择器可以包含多个视图模型,每个视图模型与不同的媒体类型相关联,允许您提供多个表示。例如,以下选择器将允许特定的控制器返回 JSON 或 HTML 输出

'selectors' => [
    'HTML-Json' => [
        'Laminas\ApiTools\ContentNegotiation\JsonModel' => [
            'application/json',
            'application/*+json',
        ],
        'Laminas\ApiTools\ContentNegotiation\ViewModel' => [
            'text/html',
        ],
    ],
],

键:accept_whitelist

accept_whitelist 键用于指示内容协商模块哪些媒体类型是特定控制器服务名称可接受的。当在此键中配置控制器服务名称以及一个匹配的媒体类型索引数组时,只有匹配给定请求的 Accept 头的媒体类型将被允许分发。不匹配的媒体类型将收到 406 Cannot honor Accept type specified 响应。

每个控制器服务名称键的值可以是字符串或字符串数组。

示例

'accept_whitelist' => [
    'Application\\Controller\\HelloApiController' => [
        'application/vnd.application-hello+json',
        'application/hal+json',
        'application/json',
    ],
],

键:content_type_whitelist

content_type_whitelist 键用于指示内容协商模块哪些媒体类型是请求的 Content-Type 有效。当在此键中配置控制器服务名称以及一个匹配的媒体类型索引数组时,只有匹配给定请求的 Content-Type 头的媒体类型将被允许分发。不匹配的媒体类型将收到 415 Invalid content-type specified 响应。

每个控制器服务名称键的值可以是字符串或字符串数组。

示例

'content_type_whitelist' => [
    'Application\\Controller\\HelloWorldController' => [
        'application/vnd.application-hello-world+json',
        'application/json',
    ],
],

键:x_http_method_override_enabled

  • 自 1.3.0 版起

这个布尔标志确定是否默认启用 HttpMethodOverrideListener

键:http_override_methods

  • 自 1.3.0 版起

http_override_methods 键用于向 HttpMethodOverrideListener 提供一个映射,该映射指定了给定 HTTP 方法允许的重写方法,这些方法是通过 X-HTTP-Method-Override 头指定的。本质上,值是

'Incoming HTTP request method' => $arrayOfAllowedOverrideMethods,

例如,如果您想允许 X-HTTP-Method-Override 头允许用备用方法重写 HTTP GET 请求,您可能可以这样定义

'x_http_method_override_enabled' => true,
'http_override_methods' => [
    'GET' => [
        'HEAD',
        'POST',
        'PUT',
        'DELETE',
        'PATCH',
    ],
];

然后,要发送请求,您可以这样做

GET /foo HTTP/1.1
Host: example.com
X-HTTP-Method-Override: PATCH

some=content&more=content

上述将解释为 PATCH 请求。如果通过 HTTP POST 发送相同的请求,或者如果带有重写值 OPTIONSGET 请求被发送,监听器将引发问题,因为在前者中未定义 POST 的映射,在后者中,OPTIONS 不在 GET 的映射中。

系统配置

以下配置在 config/module.config.php 中提供,以启用模块功能

'filters' => [
    'aliases'   => [
        'Laminas\Filter\File\RenameUpload' => 'filerenameupload',
    ],
    'factories' => [
        'filerenameupload' => Factory\RenameUploadFilterFactory::class,
    ],
],

'validators' => [
    'aliases'   => [
        'Laminas\Validator\File\UploadFile' => 'fileuploadfile',
    ],
    'factories' => [
        'fileuploadfile' => Factory\UploadFileValidatorFactory::class,
    ],
],

'service_manager' => [
    'factories' => [
        ContentTypeListener::class        => InvokableFactory::class,
        AcceptListener::class             => Factory\AcceptListenerFactory::class,
        AcceptFilterListener::class       => Factory\AcceptFilterListenerFactory::class,
        ContentTypeFilterListener::class  => Factory\ContentTypeFilterListenerFactory::class,
        ContentNegotiationOptions::class  => Factory\ContentNegotiationOptionsFactory::class,
        HttpMethodOverrideListener::class => Factory\HttpMethodOverrideListenerFactory::class,
    ],
],

'controller_plugins' => [
    'aliases' => [
        'routeParam'  => ControllerPlugin\RouteParam::class,
        'queryParam'  => ControllerPlugin\QueryParam::class,
        'bodyParam'   => ControllerPlugin\BodyParam::class,
        'routeParams' => ControllerPlugin\RouteParams::class,
        'queryParams' => ControllerPlugin\QueryParams::class,
        'bodyParams'  => ControllerPlugin\BodyParams::class,
    ],
    'factories' => [
        ControllerPlugin\RouteParam::class  => InvokableFactory::class,
        ControllerPlugin\QueryParam::class  => InvokableFactory::class,
        ControllerPlugin\BodyParam::class   => InvokableFactory::class,
        ControllerPlugin\RouteParams::class => InvokableFactory::class,
        ControllerPlugin\QueryParams::class => InvokableFactory::class,
        ControllerPlugin\BodyParams::class  => InvokableFactory::class,
    ],
],

Laminas 事件

监听器

Laminas\ApiTools\ContentNegotiation\AcceptListener

此监听器附加到 MvcEvent::EVENT_DISPATCH 事件,优先级为 -10。它负责根据内容协商配置执行控制器视图模型的实际选择和转换。

Laminas\ApiTools\ContentNegotiation\ContentTypeListener

此监听器附加到 MvcEvent::EVENT_ROUTE 事件,优先级为 -625。它负责检查 Content-Type 头以确定内容体应该如何反序列化。然后,值持久保存在 ParameterDataContainer 中,该容器存储在 MvcEvent 对象的 LaminasContentNegotiationParameterData 键中。

Laminas\ApiTools\ContentNegotiation\AcceptFilterListener

此监听器附加到 MvcEvent::EVENT_ROUTE 事件,优先级为 -625。它负责确保由路由选定的控制器配置为响应当前请求的 Accept 标头中指定的特定媒体类型。如果不能,将通过返回 406 Cannot honor Accept type specified 响应来短路 MVC 分发过程。

Laminas\ApiTools\ContentNegotiation\ContentTypeFilterListener

此监听器附加到 MvcEvent::EVENT_ROUTE 事件,优先级为 -625。它负责确保匹配的路由控制器可以接受请求体中由当前请求的 Content-Type 标头指定的媒体类型。如果不能,将通过返回 415 Invalid content-type specified 响应来短路 MVC 分发过程。

Laminas\ApiTools\ContentNegotiation\HttpMethodOverrideListener

  • 自 1.3.0 版起

此监听器附加到 MvcEvent::EVENT_ROUTE 事件,优先级为 -40,但仅当 x_http_method_override_enabled 配置标志被切换开时。它负责检查是否存在 X-HTTP-Method-Override 标头,如果存在,则检查它是否包含当前 HTTP 请求方法允许的值。如果是,则将 HTTP 请求方法重置为标头值。

Laminas 服务

控制器插件

Laminas\ApiTools\ContentNegotiation\ControllerPlugin\RouteParam (又名 "routeParam")

一个控制器插件(Laminas\Mvc\Controller\AbstractActionController 可调用),将返回路由匹配中的单个具有特定名称的参数。

use Laminas\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        return $this->routeParam('id', 'someDefaultValue');
    }
}

Laminas\ApiTools\ContentNegotiation\ControllerPlugin\QueryParam (又名 "queryParam")

一个控制器插件(Laminas\Mvc\Controller\AbstractActionController 可调用),将返回当前请求查询字符串中的单个参数。

use Laminas\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        return $this->queryParam('foo', 'someDefaultValue');
    }
}

Laminas\ApiTools\ContentNegotiation\ControllerPlugin\BodyParam (又名 "bodyParam")

一个控制器插件(Laminas\Mvc\Controller\AbstractActionController 可调用),将返回内容协商内容体中的单个参数。

use Laminas\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        return $this->bodyParam('foo', 'someDefaultValue');
    }
}

Laminas\ApiTools\ContentNegotiation\ControllerPlugin\RouteParams (又名 "routeParams")

一个控制器插件(Laminas\Mvc\Controller\AbstractActionController 可调用),将返回所有路由参数。

use Laminas\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        return $this->routeParams()
    }
}

Laminas\ApiTools\ContentNegotiation\ControllerPlugin\QueryParams (又名 "queryParams")

一个控制器插件(Laminas\Mvc\Controller\AbstractActionController 可调用),将返回所有查询参数。

use Laminas\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        return $this->queryParams()
    }
}

Laminas\ApiTools\ContentNegotiation\ControllerPlugin\BodyParams (又名 "bodyParams")

一个控制器插件(Laminas\Mvc\Controller\AbstractActionController 可调用),将返回所有内容协商体参数。

use Laminas\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        return $this->bodyParams()
    }
}