diff --git a/docs/guide-ru/rest-versioning.md b/docs/guide-ru/rest-versioning.md new file mode 100644 index 0000000000..1f780e110a --- /dev/null +++ b/docs/guide-ru/rest-versioning.md @@ -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` вы можете написать условный код в таких местах, +как действия, классы ресурсов, сериализаторы и т.д. + +Так как минорные версии требуют поддержания обратной совместимости, будем надеяться, что в вашем коде не так уж много +проверок на номер версии. В противном случае велики шансы, что вам нужна новая мажорная версия.