Handler is a core concept in the Salvo framework, which can be simply understood as a request processing unit. It has two main purposes:
As an Endpoint: Objects implementing Handler
can be placed in the routing system as endpoints to handle requests. When using the #[handler]
macro, functions can directly serve as endpoints. The #[endpoint]
macro not only allows functions to act as endpoints but also automatically generates OpenAPI documentation (this will be detailed in later documentation).
As Middleware: The same Handler
can also function as middleware, processing requests before or after they reach the final endpoint.
Salvo's request processing flow can be visualized as a "pipeline": requests first pass through a series of middleware (vertical processing) before reaching the matched endpoint (horizontal processing). Both middleware and endpoints are implementations of Handler
, ensuring system-wide consistency and flexibility.
The essence of the onion model lies in the placement of ctrl.call_next()
, enabling bidirectional processing of requests and responses. This allows each middleware to participate in the complete request-response cycle.
A Handler is an object responsible for processing Request objects. The Handler itself is a Trait containing an asynchronous handle
method:
The handle
function's default signature includes four parameters: &mut Request
, &mut Depot
, &mut Response
, and &mut FlowCtrl
. Depot is temporary storage for request-related data.
Depending on usage, a Handler can serve as middleware (hoop), processing requests before or after they reach the final Handler—e.g., for authentication, data compression, etc.
Middleware is added via the hoop
function of Router
. Added middleware affects the current Router
and all its descendant Router
s.
A Handler
can also serve as a goal—participating in routing matching and final execution.
Handler
as Middleware (hoop)When a Handler
acts as middleware, it can be added to three types of middleware-supporting objects:
Service
: All requests pass through middleware defined in Service
.Router
: Only successfully matched requests pass through middleware defined in Service
and all middleware collected along the matched path.Catcher
: When an error occurs and no custom error message is written, requests pass through middleware in Catcher
.Handler
: A Handler
itself supports adding middleware wrappers for pre- or post-processing logic.#[handler]
MacroThe #[handler]
macro significantly simplifies code and enhances flexibility.
It can be applied to a function to implement Handler
:
This is equivalent to:
As shown, using #[handler]
makes the code much simpler:
#[async_trait]
is no longer needed.Writer
or Scribe
traits can be directly returned. Here, &'static str
implements Scribe
, so it can be returned directly.The #[handler]
macro can also be applied to a struct
's impl
block to implement Handler
. In this case, the handle
function in the impl
block is recognized as the concrete implementation of Handler
's handle
:
In Salvo, a Handler
can return Result
, provided the Ok
and Err
types both implement the Writer
trait.
Given the widespread use of anyhow
, when the anyhow
feature is enabled, anyhow::Error
implements the Writer
trait and is mapped to InternalServerError
.
For custom error types, you can render different error pages as needed: