Маршрутизація

Маршрутизація

Від #[Route] до REST-маршруту

Метод RPC-процедури потрапляє в REST, коли має атрибут Symfony\Component\Routing\Attribute\Route. Атрибут на класі необов'язковий і задає спільний шлях та назву. Методи без #[Route] лишаються лише в RPC.

Частина Правило
Шлях шлях класу + / + шлях методу; порожній шлях методу дає шлях класу, обидва порожні — /
Назва ufo_rest_api_ + назва класу + _ + назва методу
Назва класу name з атрибута класу; без нього — псевдонім процедури з #[RPC\Info] у snake_case (shopItems → shop_items)
Назва методу name з атрибута методу; без нього — назва PHP-методу в snake_case (createItem → create_item)
HTTP-методи methods з атрибута методу; без них — POST
Інші параметри #[Route] requirements, defaults, host тощо не використовуються: читаються лише path, name і methods
#[RPC\Info('catalog')]
#[Route('/catalog', name: 'catalog')]
class CatalogApi implements IRpcService
{
    #[Route('/items/{id}', name: 'item', methods: ['GET'])]   // GET  /rest/catalog/items/{id}
    public function item(int $id): array {}

    #[Route('/items')]                                         // POST /rest/catalog/items
    public function createItem(string $title): array {}
}

Змінні шляху ({id}) передаються в метод як параметри з тією самою назвою — див. Параметри запиту.

Шлях не виводиться з назви RPC-методу автоматично: без #[Route] метод недоступний через REST, а невідомий шлях повертає 404.

Згенеровані маршрути

Для кожного REST-методу і версії API створюються два маршрути Symfony:

Маршрут Шлях HTTP-методи Призначення
<назва>_<версія> <шлях>, для версій, відмінних від v1, — /<версія><шлях> з атрибута Виклик методу
<назва>_<версія>_method_not_allowed той самий будь-які Пріоритет -10: неправильний HTTP-метод отримує 405, а не 404

Маршрути з config/router.yaml бандла:

Маршрут Шлях Призначення
ufo_rest_doc префікс імпорту OpenAPI, GET
ufo_rest_doc_ver /{ver} (v\d+) OpenAPI для версії
ufo_rest_doc_postman /postman Postman
ufo_rest_doc_postman_ver /{ver}/postman Postman для версії
ufo_rest_api_entrypoint /{path} Загальний вхід, пріоритет -100: невідомий шлях повертає 404 у форматі помилок
ufo_rest_api_entrypoint_ver /{ver}/{path} Те саме для версій, пріоритет -90

HEAD обробляється маршрутами GET. OPTIONS окремо не обробляється: для методу без OPTIONS у methods відповідь — 405.

Версії

Версії задає JsonRpcBundle: #[RPC\Info(version: 2, removedMethods: [...])] (Версії API). Процедура версії 2 успадковує методи версії 1, які не перевизначає і не прибирає.

Версія Виклик Документація Назва маршруту
v1 (за замовчуванням) /rest/catalog/items/1 /rest, /rest/postman ..._v1
v2 /rest/v2/catalog/items/1 /rest/v2, /rest/v2/postman ..._v2

Невідома версія повертає 404.

Групи API та кілька входів

Імпортуйте config/router.yaml окремо для кожного входу. defaults.api_group вибирає групу API; без нього група — general. Префікс URL від назви групи не залежить.

# config/routes/ufo_rest_api.yaml
rest:
    resource: ../../vendor/ufo-tech/rest-bundle/config/router.yaml
    prefix: /rest
    trailing_slash_on_root: false

rest_internal:
    resource: ../../vendor/ufo-tech/rest-bundle/config/router.yaml
    prefix: /internal
    defaults: { api_group: internal }
    trailing_slash_on_root: false

rest_partners:
    resource: ../../vendor/ufo-tech/rest-bundle/config/router.yaml
    prefix: /partners
    name_prefix: partners_
    defaults: { api_group: internal }
    trailing_slash_on_root: false
#[RPC\Info('catalog')]
#[RPC\APIGroups(['internal', 'public'], false)]
#[Route('/catalog', name: 'catalog')]
class CatalogApi implements IRpcService
{
    #[Route('/list', name: 'list', methods: ['GET'])]
    public function list(): array { return []; }

    #[RPC\APIGroups(['internal'], false)]
    #[Route('/price', name: 'price', methods: ['POST'])]
    public function price(int $price): int { return $price; }
}
  • Належність методу до груп визначає #[RPC\APIGroups]: атрибут методу замінює атрибут класу, withDefault: true додає general. #[RPC\IgnoreApi] повністю виключає метод. Щоб метод з'явився в REST, #[Route] потрібен однаково.
  • Кожен вхід має власні методи, версії, документацію і провайдер токенів.
  • Назви маршрутів отримують префікс групи: internal_ufo_rest_doc, internal_ufo_rest_api_catalog_list_v1. name_prefix іде після нього: internal_partners_ufo_rest_doc.
  • Маршрути групи general зберігають і старі назви як псевдоніми: ufo_rest_doc → general_ufo_rest_doc.
  • Групу визначає лише маршрут: поле api_group у query чи тілі запиту її не змінює.

Поруч з RPC та іншими адаптерами

Маршрути REST реєструються через Ufo\JsonRpcBundle\Routing\IApiRouteImport (клас RestRouteImport), тож обробку груп і назв виконує завантажувач маршрутів JsonRpcBundle. RPC-, REST- і маршрути інших адаптерів, що реалізують той самий інтерфейс, можна імпортувати для тих самих груп в одному файлі без конфліктів назв.

У 1.1–1.2 для цього використовувався власний завантажувач RestYamlFileLoader; у 1.3 його видалено (див. Історію змін). Застосунків, що лише імпортують config/router.yaml, це не стосується.

Діагностика

  • Методу немає в REST — перевірте #[Route] на методі, групу API імпорту, #[RPC\IgnoreApi] і removedMethods версії.
  • 405 для наявного методу — HTTP-методу немає в methods атрибута (за замовчуванням — лише POST).
  • Назви маршрутів — bin/console debug:router | grep ufo_rest.