The route directive#
<ploneasgi:route /> registers a native ASGI route from ZCML.
Namespace and declaration#
Item |
Value |
|---|---|
Namespace |
|
Directive |
|
Declared in |
|
Handler |
Include the declaration before any file that uses the directive:
<include package="ploneasgi" file="meta.zcml" />
Attributes#
Attribute |
Required |
Default |
Meaning |
|---|---|---|---|
|
yes |
Starlette path pattern, relative to the reserved prefix. It must start with |
|
|
yes |
Dotted name of the endpoint: a sync function for |
|
|
no |
|
|
|
no |
|
Space-separated HTTP methods. Ignored for websocket routes. |
|
no |
Id of the permission required on the portal, for example |
|
|
no |
Site id used when a request carries no VirtualHostMonster rewrite. |
|
|
no |
the path |
Route name, as |
|
no |
|
Must be |
Behavior#
- Mounting
A route declared with
path="/notify"is served at the reserved prefix plus the path:/++asgi++/notifyby default.- Handler call
An
httphandler is called ashandler(ctx, request)in a worker thread, wherectxis aPloneContextandrequestthe StarletteRequest. It returns a StarletteResponse. The permission check and the handler share onerun_authenticated()call: one ZODB connection, one authentication, and one snapshot per request. Awebsockethandler is called asawait handler(websocket)on the event loop, after the check.- Response
The response is sent on the event loop after the worker thread and the ZODB connection are released. A
StreamingResponseorFileResponsetherefore holds no thread for the transfer.- Request body
The body is read on the event loop before the thread hop, and handed to
ctx.request. It is held to the application'smax_body_size, published bycreate_app()asapp.state.max_body_size; a larger body gets413 Request Entity Too Largeand the connection is closed. After the permission check,ctx.request.processInputs()fillsctx.request.formfrom the query string and the body. The Starletterequest.body()cannot be awaited from the handler.- Permission check
The permission is checked against the portal, as the connecting user, before the handler runs. The id is resolved to its title through the
IPermissionutility. Anunguardedroute skips the check, but its handler still runs in an authenticated context.- Unknown permission
An id with no registered
IPermissionutility refuses every request, and the refusal is logged as an error.- Refusal
An
httprequest that fails the check gets403 Forbidden. A websocket that fails the check is closed with code1008without being accepted.- Errors from the handler
NotAuthenticatedbecomes401 Unauthorized. Zope'sUnauthorized, raised bytraverse(), becomes403 Forbidden. Zope'sBadRequest, raised by form parsing on a malformed body, becomes400 Bad Request. Any other exception propagates, and the server answers500 Internal Server Error.- Unknown site
A site path that enters no Plone site gets
404 Not Foundonhttp, and is refused on websocket.
Configuration errors#
ZCML execution fails at startup when:
typeis neitherhttpnorwebsocket;permissionis omitted andunguardedis nottrue;pathdoes not start with/;two routes of the same
typedeclare the samepath;two routes declare the same
name.
The conflict report names both file locations.
Collection#
Registrations go into the global component registry.
ploneasgi.zcml.routes_from_registry() returns them as Starlette routes, sorted by type and path.
ploneasgi.app.create_app() never calls it: the project must pass the result in routes.