handler 原理
1.1 剖析
在 actix-web 中,路由设置 handler 的方法 定义如下:
#![allow(unused)]
fn main() {
pub fn to<F, Args>(self, handler: F) -> Self
where
F: Handler<Args>,
Args: FromRequest + 'static,
F::Output: Responder + 'static,
{
self.service = handler_service(handler);
self
}
}
参数handler的约束为Handler<Args>, 其定义如下:
#![allow(unused)]
fn main() {
pub trait Handler<Args>: Clone + 'static {
type Output;
type Future: Future<Output = Self::Output>;
fn call(&self, args: Args) -> Self::Future;
}
}
这里的Args就代表n个参数.
结合这两个来看, to函数中Handler<Args>的Args需要实现FromRequest trait 就可以作为参数, 而Handler<Args>的关联类型Output需要实现Responder trait.
但是Args是如何实现动态参数的呢, 来看下to函数的handler_service方法:
#![allow(unused)]
fn main() {
pub(crate) fn handler_service<F, Args>(handler: F) -> BoxedHttpServiceFactory
where
F: Handler<Args>,
Args: FromRequest,
F::Output: Responder,
{
boxed::factory(fn_service(move |req: ServiceRequest| {
let handler = handler.clone();
async move {
let (req, mut payload) = req.into_parts();
let res = match Args::from_request(&req, &mut payload).await {
Err(err) => HttpResponse::from_error(err),
Ok(data) => handler
.call(data)
.await
.respond_to(&req)
.map_into_boxed_body(),
};
Ok(ServiceResponse::new(req, res))
}
}))
}
}
这里的handler的约束和上而的to区别不大,看下里面的实现,同上面所说Args只需要实现FromRequest就可以拿来做参数, 换句话说, 实现了 FromRequest 你可以在 Handle 方法中的任意参数位置写上你要的参数名和类型.
通过Args::FromRequest提取出参数,就调用handler.call方法执行你的接口, 如何失败则返回FromRequest中定义的Error, 所以你可以随意定义参数提取失败之后返回的内容.
但是问题来了, Args如何实现支持多个参数, 在源码里找到以下定义:
#![allow(unused)]
fn main() {
macro_rules! tuple_from_req {
($fut: ident; $($T: ident),*) => {
/// FromRequest implementation for tuple
#[allow(unused_parens)]
impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+)
{
type Error = Error;
type Future = $fut<$($T),+>;
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
$fut {
$(
$T: ExtractFuture::Future {
fut: $T::from_request(req, payload)
},
)+
}
}
}
pin_project! {
pub struct $fut<$($T: FromRequest),+> {
$(
#[pin]
$T: ExtractFuture<$T::Future, $T>,
)+
}
}
impl<$($T: FromRequest),+> Future for $fut<$($T),+>
{
type Output = Result<($($T,)+), Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
let mut ready = true;
$(
match this.$T.as_mut().project() {
ExtractProj::Future { fut } => match fut.poll(cx) {
Poll::Ready(Ok(output)) => {
let _ = this.$T.as_mut().project_replace(ExtractFuture::Done { output });
},
Poll::Ready(Err(e)) => return Poll::Ready(Err(e.into())),
Poll::Pending => ready = false,
},
ExtractProj::Done { .. } => {},
ExtractProj::Empty => unreachable!("FromRequest polled after finished"),
}
)+
if ready {
Poll::Ready(Ok(
($(
match this.$T.project_replace(ExtractFuture::Empty) {
ExtractReplaceProj::Done { output } => output,
_ => unreachable!("FromRequest polled after finished"),
},
)+)
))
} else {
Poll::Pending
}
}
}
};
}
impl FromRequest for () {
type Error = Infallible;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
ok(())
}
}
tuple_from_req! { TupleFromRequest1; A }
tuple_from_req! { TupleFromRequest2; A, B }
tuple_from_req! { TupleFromRequest3; A, B, C }
tuple_from_req! { TupleFromRequest4; A, B, C, D }
tuple_from_req! { TupleFromRequest5; A, B, C, D, E }
tuple_from_req! { TupleFromRequest6; A, B, C, D, E, F }
tuple_from_req! { TupleFromRequest7; A, B, C, D, E, F, G }
tuple_from_req! { TupleFromRequest8; A, B, C, D, E, F, G, H }
tuple_from_req! { TupleFromRequest9; A, B, C, D, E, F, G, H, I }
tuple_from_req! { TupleFromRequest10; A, B, C, D, E, F, G, H, I, J }
tuple_from_req! { TupleFromRequest11; A, B, C, D, E, F, G, H, I, J, K }
tuple_from_req! { TupleFromRequest12; A, B, C, D, E, F, G, H, I, J, K, L }
}
这里把Args的一堆 tuple, (),A,A, B…都实现了,所以在 handler_service 里的 Args 实际上是一个 tuple,并在里面逐个调用参数的from_request, 接口的参数限制最多12个, 当超过12个参数时就会报错. 这就回答了上面Args支持多个参数.
这里又出新了一个新的问题, 参数解析出来了, 但是 handler.call(data)如何支持多个参数, 看实现, Handler<Args> 给fn各种长度参数fn() -> R, fn(A, B, C) -> R 实现了最多12个参数.
#![allow(unused)]
fn main() {
/// Generates a [`Handler`] trait impl for N-ary functions where N is specified with a sequence of
/// space separated type parameters.
///
/// # Examples
/// ```ignore
/// factory_tuple! {} // implements Handler for types: fn() -> R
/// factory_tuple! { A B C } // implements Handler for types: fn(A, B, C) -> R
/// ```
macro_rules! factory_tuple ({ $($param:ident)* } => {
impl<Func, Fut, $($param,)*> Handler<($($param,)*)> for Func
where
Func: Fn($($param),*) -> Fut + Clone + 'static,
Fut: Future,
{
type Output = Fut::Output;
type Future = Fut;
#[inline]
#[allow(non_snake_case)]
fn call(&self, ($($param,)*): ($($param,)*)) -> Self::Future {
(self)($($param,)*)
}
}
});
factory_tuple! {}
factory_tuple! { A }
factory_tuple! { A B }
factory_tuple! { A B C }
factory_tuple! { A B C D }
factory_tuple! { A B C D E }
factory_tuple! { A B C D E F }
factory_tuple! { A B C D E F G }
factory_tuple! { A B C D E F G H }
factory_tuple! { A B C D E F G H I }
factory_tuple! { A B C D E F G H I J }
factory_tuple! { A B C D E F G H I J K }
factory_tuple! { A B C D E F G H I J K L }
}