mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-01 03:26:36 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			113 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| Версионирование
 | ||
| ===============
 | ||
| 
 | ||
| Хороший API должен быть *версионирован*: изменения и новые возможности реализуются в новых версиях API, а не в одной и
 | ||
| той же версии. В отличие от Web-приложений, где у вас есть полный контроль и над серверным, и над клиентским кодом,
 | ||
| API используются клиентами, код которых вы не контролируете. Поэтому, обратная совместимость (BC) должна по возможности
 | ||
| сохраняться. Если ломающее её изменение необходимо, делать его нужно в новой версии API. Существующие клиенты могут
 | ||
| продолжать использовать старую, совместимую с ними версию API. Новые или обновлённые клиенты могут использовать новую
 | ||
| версию. 
 | ||
| 
 | ||
| > Tip: Чтобы узнать больше о выборе версий обратитесь к [Semantic Versioning](https://semver.org/).
 | ||
| 
 | ||
| Общей практикой при реализации версионирования 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
 | ||
|             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']],
 | ||
|             ],
 | ||
|         ],
 | ||
|     ],
 | ||
| ];
 | ||
| ```
 | ||
| 
 | ||
| В результате `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` вы можете выбирать поведение в действиях, классах ресурсов,
 | ||
| сериализаторах и т.д.
 | ||
| 
 | ||
| Так как минорные версии требуют поддержания обратной совместимости, будем надеяться, что в вашем коде не так уж много
 | ||
| проверок на номер версии. В противном случае велики шансы, что вам нужна новая мажорная версия.
 | 
