如何在 actix-web 2.0 中间件中获取 web::Data<Pool<MySql>> ?

Anu*_*aki 2 middleware rust actix-web

通常,我可以像这样设置数据App并从中获取数据:web::Data

let pool = sqlx::MySqlPool::connect("mysql://xxx")
    .await
    .expect("Mysql Connect error!");
HttpServer::new(move || {
    // set the data pool: Pool<MySQL>
    App::new()
        .data(pool.clone())
        .service(web::resource("/home").route(web::get().to(get_xxx)))
})
.bind("0.0.0.0:8000")?
.run()
.await;
Run Code Online (Sandbox Code Playgroud)
// get the data: pool: Pool<MySQL> from argument.
pub async fn get_xxx(
    pool: web::Data<Pool<MySql>>,
    path: web::Path<String>,
) -> Result<HttpResponse, Error> {
    let mut pool = pool.clone();
    todo!()
}
Run Code Online (Sandbox Code Playgroud)

如何获取pool: Pool<MySQL>中间件?

这是中间件的示例:

use std::task::{Context, Poll};

use actix_service::{Service, Transform};
use actix_web::dev::{ServiceRequest, ServiceResponse};
use actix_web::{Error, HttpResponse};
use futures::future::{ok, Either, Ready};

pub struct CheckLogin;

impl<S, B> Transform<S> for CheckLogin
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = CheckLoginMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(CheckLoginMiddleware { service })
    }
}
pub struct CheckLoginMiddleware<S> {
    service: S,
}

use actix_web::http::HeaderValue;
impl<S, B> Service for CheckLoginMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Either<S::Future, Ready<Result<Self::Response, Self::Error>>>;

    fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        let pool = todo!(); // here! how can I get the pool: Pool<MySQL>
    }
}
Run Code Online (Sandbox Code Playgroud)

我不知道 actix-web 如何将参数传递给最终的路由函数。

Qua*_*Cat 7

我想req.app_data()这就是你想要的。

fn call(&mut self, req: ServiceRequest) -> Self::Future {
    let pool = req.app_data::<web::Data<Pool<MySql>>>().unwrap();
    // ...
}
Run Code Online (Sandbox Code Playgroud)

当您调用 时App::data(),actix-web 会将给定值存储到内部映射中。关键是它的类型。当您致电 时app_data(),您会尝试从这张地图中获取价值。

我不知道 actix-web 如何将参数传递给最终的路由函数。

最终的路由函数只是调用<ParamType as FromRequest>::from_request来获取值。例如,当您尝试获取时会发生以下情况web::Data

fn call(&mut self, req: ServiceRequest) -> Self::Future {
    let pool = req.app_data::<web::Data<Pool<MySql>>>().unwrap();
    // ...
}
Run Code Online (Sandbox Code Playgroud)

您可以看到在内部from_request,从web::Data获取值(您的web::Data<T>参数)req.app_data()

顺便说一句,我注意到你正在使用sqlx,它Pool实际上有一个Arc内部。但Data也有Arc内在。这是多余的。如果您介意这一点,您可以定义 的包装类型Pool,并FromRequest为其实现特征。