docs: rewrite JsonRpcBundle docs, add SDK docs, move them under docs/api
uk/docs/api/rest-bundle/developers/lifecycle.md
новий файл +51
−0
1
+
# Життєвий цикл запиту
2
+
3
+
Як REST Bundle обробляє запит — від HTTP-запиту до RPC-виклику і назад. Опис відповідає версії 1.3; як той самий виклик обробляє ядро, — на сторінці [Життєвий цикл JsonRpcBundle](/docs/api/json-rpc-bundle/developers/lifecycle).
4
+
5
+
## REST-виклик
6
+
7
+
1. **`kernel.request`, пріоритет 32 — `RouterListener`** знаходить згенерований REST-маршрут. Його defaults містять `_rest_route` (назва REST-маршруту без версії), `_ver` для версій, відмінних від `v1`, `api_group` і `_rest_name_prefix`.
8
+
2. **`kernel.request`, пріоритет 30 — `RestControllerListener`** записує групу в `ApiGroupContext` і для REST-виклику спрямовує `_controller` на `RestEntryPointController::rest` з атрибутами `rpcRoute` і `ver`.
9
+
3. **`RestRequestParser::parse()`**
10
+
* знаходить REST-маршрут і RPC `Service` для групи та версії (`404`, якщо немає);
11
+
* перевіряє HTTP-метод: `HEAD` вважається `GET`, недозволений метод — `405`;
12
+
* скидає пул RPC-подій (`RpcEventFactory::reset()`), встановлює `HttpTokenHolder` і carrier `RpcFromRest`, перевіряє токен (`protected_api`).
13
+
4. **`RpcFromRest`** будує `RpcRequest`: згенерований `id`, назва RPC-методу, об'єднані [параметри](/docs/api/rest-bundle/requests) і `apiVersion`.
14
+
5. **`RpcExecutor::execute()`** виконує запит так само, як JSON-RPC: `rpc.request` → `rpc.pre_execute` (формування аргументів, валідація, конвертація, `#[RPC\Lock]`, виклик методу) → `rpc.pre_response` → `rpc.post_execute`. Будь-яка помилка проходить через `rpc.error` і стає відповіддю з помилкою.
15
+
6. **Контролер** повертає `200` з результатом методу або перетворює помилку на [RFC 9457 problem](/docs/api/rest-bundle/error_handling) через `RestErrorNormalizer`.
16
+
7. **`kernel.terminate`** — для успішного виклику генерується `rpc.post_response` (інвалідація `#[RPC\CacheRelation]`, post-response лістенери). Для помилки — ні.
17
+
18
+
Запит без відповідного REST-маршруту потрапляє в загальний вхід `ufo_rest_api_entrypoint` (пріоритет `-100`), і парсер повертає `404`. Запит з неправильним HTTP-методом потрапляє в маршрут `<назва>_method_not_allowed` (пріоритет `-10`), і парсер повертає `405`.
19
+
20
+
## Документація
21
+
22
+
1. Пріоритет 32 — роутинг на маршрут з default `_rest_doc`.
23
+
2. Пріоритет 30 — `RestControllerListener` записує групу в `ApiGroupContext`.
24
+
3. Пріоритет 8 — `RestDocLocaleListener` вибирає мову з `Accept-Language` (`RpcLocaleNegotiator::applyLocale()`).
25
+
4. `RestEntryPointController::openapiAction()` / `postmanAction()` перевіряє токен (`protected_doc`) і будує документ для версії та групи.
26
+
5. `kernel.response` — `Content-Language` і `Vary: Accept-Language`.
27
+
28
+
RPC-події на цьому шляху не генеруються; документ будується на кожен запит.
29
+
30
+
## Події
31
+
32
+
REST-виклик генерує ті самі події, що й JSON-RPC, тож лістенери застосунку працюють для обох:
33
+
34
+
| Подія | Константа | Коли |
35
+
| --- | --- | --- |
36
+
| `rpc.request` | `RpcEvent::REQUEST` | запит прийнято |
37
+
| `rpc.pre_execute` | `RpcEvent::PRE_EXECUTE` | перед методом |
38
+
| `rpc.pre_response` | `RpcEvent::PRE_RESPONSE` | відповідь сформовано |
39
+
| `rpc.post_execute` | `RpcEvent::POST_EXECUTE` | після методу |
40
+
| `rpc.error` | `RpcEvent::ERROR` | будь-яка помилка |
41
+
| `rpc.post_response` | `RpcEvent::POST_RESPONSE` | на `kernel.terminate`, після успішного виклику |
42
+
43
+
Детально про кожну подію — у розділі [Події життєвого циклу](/docs/api/json-rpc-bundle/functionality/events).
44
+
45
+
:::info
46
+
До 1.3 REST-контролер виконував запит через `RpcRequestHandler` без `RpcExecutor`, і `rpc.post_response` для REST не генерувався.
47
+
:::
48
+
49
+
## Стан між запитами
50
+
51
+
Група API зберігається в `ApiGroupContext`, carrier — у `RequestCarrier`, токен-холдер — в `IRpcSecurity`; ці сервіси належать JsonRpcBundle і скидаються між запитами в довгоживучих воркерах. `RestServiceMap` будує REST-маршрути для групи один раз і перемикається між уже побудованими мапами, коли змінюється група в `ApiGroupContext`.