Request lifecycle

Request lifecycle

How REST Bundle processes a request: from the HTTP request to the RPC call and back. The description matches version 1.3; how the core processes the same call is described on the JsonRpcBundle lifecycle page.

REST call

  1. kernel.request, priority 32: RouterListener finds the generated REST route. Its defaults contain _rest_route (the REST route name without the version), _ver for versions other than v1, api_group and _rest_name_prefix.
  2. kernel.request, priority 30: RestControllerListener writes the group to ApiGroupContext and, for a REST call, points _controller to RestEntryPointController::rest with the rpcRoute and ver attributes.
  3. RestRequestParser::parse()
    • finds the REST route and the RPC Service for the group and version (404 if missing);
    • checks the HTTP method: HEAD counts as GET, a method that is not allowed gives 405;
    • resets the RPC event pool (RpcEventFactory::reset()), sets HttpTokenHolder and the RpcFromRest carrier, checks the token (protected_api).
  4. RpcFromRest builds an RpcRequest: a generated id, the RPC method name, the merged parameters and apiVersion.
  5. RpcExecutor::execute() executes the request the same way as JSON-RPC: rpc.request → rpc.pre_execute (building arguments, validation, conversion, #[RPC\Lock], method call) → rpc.pre_response → rpc.post_execute. Any error goes through rpc.error and becomes an error response.
  6. The controller returns 200 with the method result or converts the error into an RFC 9457 problem via RestErrorNormalizer.
  7. kernel.terminate: rpc.post_response fires for a successful call (#[RPC\CacheRelation] invalidation, post-response listeners). Not for an error.

A request without a matching REST route lands in the catch-all entry point ufo_rest_api_entrypoint (priority -100), and the parser returns 404. A request with a wrong HTTP method lands in the <name>_method_not_allowed route (priority -10), and the parser returns 405.

Documentation

  1. Priority 32: routing to a route with the _rest_doc default.
  2. Priority 30: RestControllerListener writes the group to ApiGroupContext.
  3. Priority 8: RestDocLocaleListener picks the language from Accept-Language (RpcLocaleNegotiator::applyLocale()).
  4. RestEntryPointController::openapiAction() / postmanAction() checks the token (protected_doc) and builds the document for the version and group.
  5. kernel.response: Content-Language and Vary: Accept-Language.

No RPC events are fired on this path; the document is built on every request.

Events

A REST call fires the same events as JSON-RPC, so the application's listeners work for both:

Event Constant When
rpc.request RpcEvent::REQUEST the request is accepted
rpc.pre_execute RpcEvent::PRE_EXECUTE before the method
rpc.pre_response RpcEvent::PRE_RESPONSE the response is formed
rpc.post_execute RpcEvent::POST_EXECUTE after the method
rpc.error RpcEvent::ERROR any error
rpc.post_response RpcEvent::POST_RESPONSE on kernel.terminate, after a successful call

Each event is described in detail in Lifecycle events.

Before 1.3 the REST controller executed the request through RpcRequestHandler without RpcExecutor, and rpc.post_response was not fired for REST.

State between requests

The API group is kept in ApiGroupContext, the carrier in RequestCarrier, the token holder in IRpcSecurity; these services belong to JsonRpcBundle and are reset between requests in long-running workers. RestServiceMap builds the REST routes for a group once and switches between already built maps when the group in ApiGroupContext changes.