Files
yii2/docs/guide-ru/rest-versioning.md
2022-08-03 12:32:18 +03:00

113 lines
7.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Версионирование
===============
Хороший API должен быть *версионирован*: изменения и новые возможности реализуются в новых версиях API, а не в одной и
той же версии. В отличие от Web-приложений, где у вас есть полный контроль и над серверным, и над клиентским кодом,
API используются клиентами, код которых вы не контролируете. Поэтому, обратная совместимость (BC) должна по возможности
сохраняться. Если ломающее её изменение необходимо, делать его нужно в новой версии API. Существующие клиенты могут
продолжать использовать старую, совместимую с ними версию API. Новые или обновлённые клиенты могут использовать новую
версию.
> Tip: Чтобы узнать больше о выборе версий обратитесь к [Semantic Versioning](https://semver.org/).
Общей практикой при реализации версионирования API является включение номера версии в URL-адрес вызова API-метода.
Например, `https://example.com/v1/users` означает вызов API `/users` версии 1. Другой способ версионирования API,
получивший недавно широкое распространение, состоит в добавлении номера версии в HTTP-заголовки запроса,
обычно в заголовок `Accept`:
```
// как параметр
Accept: application/json; version=v1
// как тип содержимого, определенный поставщиком API
Accept: application/vnd.company.myapp-v1+json
```
Оба способа имеют достоинства и недостатки, и вокруг них много споров. Ниже мы опишем реально работающую стратегию
версионирования API, которая является некоторой смесью этих двух способов:
* Помещать каждую мажорную версию реализации API в отдельный модуль, чей ID является номером мажорной версии (например, `v1`, `v2`).
Естественно, URL-адреса API будут содержать в себе номера мажорных версий.
* В пределах каждой мажорной версии (т.е. внутри соответствующего модуля) использовать HTTP-заголовок `Accept`
для определения номера минорной версии и писать условный код для соответствующих минорных версий.
В каждый модуль, обслуживающий мажорную версию, следует включать классы ресурсов и контроллеров,
обслуживающих эту конкретную версию. Для лучшего разделения ответственности кода вы можете составить общий набор
базовых классов ресурсов и контроллеров, и субклассировать их в каждом отдельно взятом модуле версии. Внутри дочерних классов
реализуйте конкретный код вроде метода `Model::fields()`.
Ваш код может быть организован примерно следующим образом:
```
api/
common/
controllers/
UserController.php
PostController.php
models/
User.php
Post.php
modules/
v1/
controllers/
UserController.php
PostController.php
models/
User.php
Post.php
Module.php
v2/
controllers/
UserController.php
PostController.php
models/
User.php
Post.php
Module.php
```
Конфигурация вашего приложения могла бы выглядеть так:
```php
return [
'modules' => [
'v1' => [
'class' => 'app\modules\v1\Module',
],
'v2' => [
'class' => 'app\modules\v2\Module',
],
],
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user', 'v1/post']],
['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user', 'v2/post']],
],
],
],
];
```
В результате `https://example.com/v1/users` возвратит список пользователей API версии 1, в то время как
`https://example.com/v2/users` вернет список пользователей версии 2.
Благодаря использованию модулей код API различных мажорных версий может быть хорошо изолирован. И по-прежнему возможно
повторное использование кода между модулями через общие базовые классы и другие разделяемые классы.
Для работы с минорными номерами версий вы можете использовать преимущества согласования содержимого,
предоставляемого поведением [[yii\filters\ContentNegotiator|contentNegotiator]].
Определив тип поддерживаемого содержимого, поведение `contentNegotiator` установит значение
свойства [[yii\web\Response::acceptParams]].
Например, если запрос посылается с HTTP-заголовком `Accept: application/json; version=v1`, то после согласования содержимого
свойство [[yii\web\Response::acceptParams]] будет содержать значение `['version' => 'v1']`.
На основе информации о версии из `acceptParams` вы можете выбирать поведение в действиях, классах ресурсов,
сериализаторах и т.д.
Так как минорные версии требуют поддержания обратной совместимости, будем надеяться, что в вашем коде не так уж много
проверок на номер версии. В противном случае велики шансы, что вам нужна новая мажорная версия.