Controllers and namespaces
Create exact routes with explicit action and hook declarations, and learn which phase each controller metadata item applies to.
A controller groups actions belonging to a namespace. The namespace is selected when the connection starts; each message event calls an exact route within that namespace.
Canonical controller#
Use #[derive(WebSocketController)], #[namespace("hello")], fallible WebSocketControllerTrait::new, and #[websocket_controller] impl together. The complete first application demonstrates this pattern.
#[websocket_controller]
impl HelloController {
#[message("ping")]
async fn ping(&self) -> Result<Ack<&'static str>, WebSocketActionError> {
Ok(Ack::new("pong"))
}
}Add this snippet as an action in the existing macro impl, retaining the example's imports. The incoming event must be hello:ping and carry an ack_id for the ACK. Put helper methods in a separate, ordinary impl block. Actions under the macro are not discovered automatically from their names; declare #[message(...)] explicitly.
Route rules#
A namespace is at most 128 ASCII bytes. Namespace and action tokens contain letters, digits, -, _, and .. Although an action token can be up to 256 bytes, the combined namespace:event must also be at most 256 bytes. Matching is case-sensitive; hello:Greet is a different route. / does not mean a wildcard.
Duplicate namespaces or action collisions stop build. Each controller can define at most one connected and one disconnected hook.
Metadata placement#
| Metadata | Controller | Action | Lifecycle hook |
|---|---|---|---|
| Handshake / connection middleware | Yes | No | No |
| Message middleware / guard | Start of the message plan | Additional local policy | No |
| Frame codec | Namespace default | No | No |
| Payload codec | Message default | Override | No |
| Timeout | Message default | Override | Independent hook-specific duration |
| AsyncAPI | Documentation metadata | Documentation metadata | Not interpreted as a message operation |
Registration examples appear on the middleware, guards, codecs, and AsyncAPI pages. Repeating the same concrete type in an inherited plan can cause a duplicate error.
Controller lifetime#
The constructor runs once per application. Finding a registration at link time does not mean there is one live controller instance for the whole process; separate WsApp instances create separate controller instances.
Retain shared Singleton services in controller fields. Resolve message-scoped services in the action through Service<T> or through the active exchange. For mutable fields, account for multiple connections accessing the same controller; sequential processing per connection does not mean every controller invocation runs in one global sequence.
Sources for this page v0.1.0
This guide is based on the reviewed framework commit.