pub trait Handler<Args>: Clone + 'static {
type Output;
type Future: Future<Output = Self::Output>;
// Required method
fn call(&self, args: Args) -> Self::Future;
}
Expand description
The interface for request handlers.
What Is A Request Handler
In short, a handler is just an async function that receives request-based arguments, in any order, and returns something that can be converted to a response.
In particular, a request handler has three requirements:
- It is an async function (or a function/closure that returns an appropriate future);
- The function parameters (up to 12) implement
FromRequest
; - The async function (or future) resolves to a type that can be converted into an
HttpResponse
(i.e., it implements theResponder
trait).
Compiler Errors
If you get the error the trait Handler<_> is not implemented
, then your handler does not
fulfill the first of the above requirements. Missing other requirements manifest as errors on
implementing FromRequest
and Responder
, respectively.
How Do Handlers Receive Variable Numbers Of Arguments
Rest assured there is no macro magic here; it’s just traits.
The first thing to note is that FromRequest
is implemented for tuples (up to 12 in length).
Secondly, the Handler
trait is implemented for functions (up to an arity of 12) in a way
that aligns their parameter positions with a corresponding tuple of types (becoming the Args
type parameter for this trait).
Thanks to Rust’s type system, Actix Web can infer the function parameter types. During the
extraction step, the parameter types are described as a tuple type, from_request
is run on
that tuple, and the Handler::call
implementation for that particular function arity
destructures the tuple into its component types and calls your handler function with them.
In pseudo-code the process looks something like this:
async fn my_handler(body: String, state: web::Data<MyState>) -> impl Responder {
...
}
// the function params above described as a tuple, names do not matter, only position
type InferredMyHandlerArgs = (String, web::Data<MyState>);
// create tuple of arguments to be passed to handler
let args = InferredMyHandlerArgs::from_request(&request, &payload).await;
// call handler with argument tuple
let response = Handler::call(&my_handler, args).await;
// which is effectively...
let (body, state) = args;
let response = my_handler(body, state).await;
This is the source code for the 2-parameter implementation of Handler
to help illustrate the
bounds of the handler call after argument extraction:
impl<Func, Arg1, Arg2, Fut> Handler<(Arg1, Arg2)> for Func
where
Func: Fn(Arg1, Arg2) -> Fut + Clone + 'static,
Fut: Future,
{
type Output = Fut::Output;
type Future = Fut;
fn call(&self, (arg1, arg2): (Arg1, Arg2)) -> Self::Future {
(self)(arg1, arg2)
}
}