如何在 actix-web 2.0 中间件中获取 web::Data<Pool<MySql>>?
How can I get web::Data<Pool<MySql>> in actix-web 2.0 middleware?
通常,我可以像这样在 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;
// 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!()
}
如何在中间件中获取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>
}
}
我不知道actix-web如何将参数传递给最终的路由函数。
我觉得req.app_data()
就是你想要的。
fn call(&mut self, req: ServiceRequest) -> Self::Future {
let pool = req.app_data::<web::Data<Pool<MySql>>>().unwrap();
// ...
}
当您调用 App::data()
时,actix-web 会将给定值存储到内部映射中。关键是它的类型。当您调用 app_data()
时,您尝试从该映射中获取值。
I don't know how actix-web passes the arguments to the final route function.
最后的路由函数只是调用 <ParamType as FromRequest>::from_request
来获取值。例如,当您尝试获取 web::Data
:
时发生的情况
impl<T: 'static> FromRequest for Data<T> {
type Config = ();
type Error = Error;
type Future = Ready<Result<Self, Error>>;
#[inline]
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
if let Some(st) = req.app_data::<Data<T>>() {
ok(st.clone())
} else {
log::debug!(
"Failed to construct App-level Data extractor. \
Request path: {:?}",
req.path()
);
err(ErrorInternalServerError(
"App data is not configured, to configure use App::data()",
))
}
}
}
您可以看到在 from_request
中,web::Data
从 req.app_data()
.
获取值(您的 web::Data<T>
参数)
顺便说一句,我注意到你用的是sqlx
,它的Pool
里面其实有一个Arc
。但是Data
里面还有一个Arc
。这是多余的。如果你介意这一点,你可以定义一个 Pool
的包装器类型,并为其实现 FromRequest
特性。
通常,我可以像这样在 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;
// 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!()
}
如何在中间件中获取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>
}
}
我不知道actix-web如何将参数传递给最终的路由函数。
我觉得req.app_data()
就是你想要的。
fn call(&mut self, req: ServiceRequest) -> Self::Future {
let pool = req.app_data::<web::Data<Pool<MySql>>>().unwrap();
// ...
}
当您调用 App::data()
时,actix-web 会将给定值存储到内部映射中。关键是它的类型。当您调用 app_data()
时,您尝试从该映射中获取值。
I don't know how actix-web passes the arguments to the final route function.
最后的路由函数只是调用 <ParamType as FromRequest>::from_request
来获取值。例如,当您尝试获取 web::Data
:
impl<T: 'static> FromRequest for Data<T> {
type Config = ();
type Error = Error;
type Future = Ready<Result<Self, Error>>;
#[inline]
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
if let Some(st) = req.app_data::<Data<T>>() {
ok(st.clone())
} else {
log::debug!(
"Failed to construct App-level Data extractor. \
Request path: {:?}",
req.path()
);
err(ErrorInternalServerError(
"App data is not configured, to configure use App::data()",
))
}
}
}
您可以看到在 from_request
中,web::Data
从 req.app_data()
.
web::Data<T>
参数)
顺便说一句,我注意到你用的是sqlx
,它的Pool
里面其实有一个Arc
。但是Data
里面还有一个Arc
。这是多余的。如果你介意这一点,你可以定义一个 Pool
的包装器类型,并为其实现 FromRequest
特性。