Handler
What is Handler
Handler is the specific object responsible for processing Request requests. Handler itself is a Trait, which contains an asynchronous method of handle
:
#[async_trait]
pub trait Handler: Send + Sync + 'static {
async fn handle(&self, req: &mut Request, depot: &mut Depot, res: &mut Response);
}
The default signature of the handle
function contains four parameters, in order, &mut Request, &mut Depot. &mut Response, &mut FlowCtrl
. Depot is a temporary storage that can store data related to this request.
Middleware is actually also a Handler
. They can do some processing before or after the request reaches the Handler
that officially handles the request, such as: login verification, data compression, etc.
Middleware is added through the hoop
function of Router
. The added middleware will affect the current Router
and all its internal descendants Router
.
Handler
as middleware
When Handler
is used as middleware, it can be added to the following three types of objects that support middleware:
Service
, any request will pass through middlewares inService
.Router
, only when the route matching is successful, the request will pass through middlewares defined inService
and all middlewares collected on the matching path.Catcher
, when an error occurs and no custom error information is written, the request will pass through the middleware inCatcher
.
#[handler]
Macro #[handler]
can greatly simplify the writing of the code, and improve the flexibility of the code.
It can be added to a function to make it implement Handler
:
#[handler]
async fn hello() -> &'static str {
"hello world!"
}
This is equivalent to:
struct hello;
#[async_trait]
impl Handler for hello {
async fn handle(&self, _req: &mut Request, _depot: &mut Depot, res: &mut Response, _ctrl: &mut FlowCtrl) {
res.render(Text::Plain("hello world!"));
}
}
As you can see, in the case of using #[handler]
, the code becomes much simpler:
- No need to manually add
#[async_trait]
. - The parameters that are not needed in the function have been omitted, and the required parameters can be arranged in any order.
- For objects that implement
Writer
orScribe
abstraction, it can be directly used as the return value of the function. Here&'static str
implementsScribe
, so it can be returned directly as the return value of the function.
#[handler]
can not only be added to the function, but also can be added to the impl
of struct
to let struct
implement Handler
. At this time, the handle
function in the impl
code block will be Identified as the specific implementation of handle
in Handler
:
struct Hello;
#[handler]
impl Hello {
async fn handle(&self, res: &mut Response) {
res.render(Text::Plain("hello world!"));
}
}
Handle errors
Handler
in Salvo can return Result
, only the types of Ok
and Err
in Result
are implemented Writer
trait. Taking into account the widespread use of anyhow
, the Writer
implementation of anyhow::Error
is provided by default, and anyhow::Error
is Mapped to InternalServerError
.
#[cfg(feature = "anyhow")]
#[async_trait]
impl Writer for ::anyhow::Error {
async fn write(mut self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
res.render(StatusError::internal_server_error());
}
}
For custom error types, you can output different error pages according to your needs.
use salvo::anyhow;
use salvo::prelude::*;
struct CustomError;
#[async_trait]
impl Writer for CustomError {
async fn write(mut self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
res.status_code(StatusCode::INTERNAL_SERVER_ERROR);
res.render("custom error");
}
}
#[handler]
async fn handle_anyhow() -> Result<(), anyhow::Error> {
Err(anyhow::anyhow!("anyhow error"))
}
#[handler]
async fn handle_custom() -> Result<(), CustomError> {
Err(CustomError)
}
#[tokio::main]
async fn main() {
let router = Router::new()
.push(Router::new().path("anyhow").get(handle_anyhow))
.push(Router::new().path("custom").get(handle_custom));
let acceptor = TcpListener::new("127.0.0.1:5800").bind().await;
Server::new(acceptor).serve(router).await;
}
Implement Handler trait directly
Under certain circumstances, We need to implment Handler
direclty.
use salvo_core::prelude::*;
use crate::salvo_core::http::Body;
pub struct MaxSizeHandler(u64);
#[async_trait]
impl Handler for MaxSizeHandler {
async fn handle(&self, req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
if let Some(upper) = req.body().size_hint().upper() {
if upper > self.0 {
res.render(StatusError::payload_too_large());
ctrl.skip_rest();
} else {
ctrl.call_next(req, depot, res).await;
}
}
}
}