Alexander Makarov
10 years ago
1 changed files with 107 additions and 0 deletions
@ -0,0 +1,107 @@ |
|||||||
|
Версионирование |
||||||
|
=============== |
||||||
|
|
||||||
|
Свои API вам следует версионировать. В отличие от Web-приложений, где у вас есть полный контроль и над серверным, и над клиентским кодом, |
||||||
|
при работе с API у вас обычно нет контроля над клиентским кодом, работающим с вашими API. Поэтому всегда, когда это только возможно, |
||||||
|
должна поддерживаться обратная совместимость API, и если в API должны быть внесены изменения, ломающие обратную совместимость, |
||||||
|
следует увеличить номер версии. Почитайте про [семантическое версионирование](http://semver.org/) |
||||||
|
для получения более подробной информации о возможных способах нумерации различных версий ваших API. |
||||||
|
|
||||||
|
Общей практикой при реализации версионирования API является включение номера версии в URL-адрес вызова API-метода. |
||||||
|
Например, `http://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 |
||||||
|
v2/ |
||||||
|
controllers/ |
||||||
|
UserController.php |
||||||
|
PostController.php |
||||||
|
models/ |
||||||
|
User.php |
||||||
|
Post.php |
||||||
|
``` |
||||||
|
|
||||||
|
Конфигурация вашего приложения могла бы выглядеть так: |
||||||
|
|
||||||
|
```php |
||||||
|
return [ |
||||||
|
'modules' => [ |
||||||
|
'v1' => [ |
||||||
|
'basePath' => '@app/modules/v1', |
||||||
|
], |
||||||
|
'v2' => [ |
||||||
|
'basePath' => '@app/modules/v2', |
||||||
|
], |
||||||
|
], |
||||||
|
'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']], |
||||||
|
], |
||||||
|
], |
||||||
|
], |
||||||
|
]; |
||||||
|
``` |
||||||
|
|
||||||
|
В результате `http://example.com/v1/users` возвратит список пользователей API версии 1, в то время как |
||||||
|
`http://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` вы можете написать условный код в таких местах, |
||||||
|
как действия, классы ресурсов, сериализаторы и т.д. |
||||||
|
|
||||||
|
Так как минорные версии требуют поддержания обратной совместимости, будем надеяться, что в вашем коде не так уж много |
||||||
|
проверок на номер версии. В противном случае велики шансы, что вам нужна новая мажорная версия. |
Loading…
Reference in new issue