Actix中间件中异步函数的生命周期问题求助
Actix-web JWT中间件生命周期问题解决
问题场景
使用Rust的actix和sqlx构建API,开发JWT中间件时,尝试将授权用户信息存入请求扩展并传递到端点,代码实现时遇到生命周期错误。
问题代码
fn call(&self, req: ServiceRequest) -> Self::Future { if ApiEnv::skip_auth() { return Box::pin(self.service.call(req)); } Box::pin(async move { let u = { let db: &Data<Database> = req.app_data().unwrap(); let pool = db.pool.clone(); let headers = req.headers().clone(); let mut extensions = req.extensions_mut(); match authorize(pool, headers).await { Ok(u) => { extensions.insert(u); Ok(()) } Err(e) => Err(e), } }; match u { Ok(_) => self.service.call(req).await, Err(e) => Err(e), } }) }
错误信息
error: lifetime may not live long enough --> src/middleware/jwt.rs | | fn call(&self, req: ServiceRequest) -> Self::Future { | - let's call the lifetime of this reference `'1` ... | / Box::pin(async move { | | let u = { | | let db: &Data<Database> = req.app_data().unwrap(); | | let pool = db.pool.clone(); ... | | | } | | }) | |______^ returning this value requires that `'1` must outlive `'static`
问题核心:Box::pin内部调用self.service.call导致生命周期不匹配;若将self.service.call(req)移到外部,req会提前被移动,无法修改扩展插入用户信息。
解决方案
问题根源是async move闭包捕获了&self,而Actix中间件的Future要求'static生命周期,需将self.service转为可安全在异步闭包中持有的类型。
方案1:用Arc包裹Service
调整中间件结构体,用Arc包裹Service,通过克隆Arc让闭包拥有独立的Service所有权:
use std::sync::Arc; // 中间件结构体定义 struct JwtMiddleware<S> { service: Arc<S>, } // 实现call方法 fn call(&self, req: ServiceRequest) -> Self::Future { if ApiEnv::skip_auth() { return Box::pin(self.service.call(req)); } // 克隆Arc,让闭包持有独立的Service引用 let service = self.service.clone(); Box::pin(async move { let auth_result = { let db: &Data<Database> = req.app_data().unwrap(); let pool = db.pool.clone(); let headers = req.headers().clone(); let mut extensions = req.extensions_mut(); match authorize(pool, headers).await { Ok(user) => { extensions.insert(user); Ok(()) } Err(e) => Err(e), } }; match auth_result { Ok(_) => service.call(req).await, Err(e) => Err(e.into()), } }) }
方案2:用ServiceExt::boxed转换Service
利用Actix提供的boxed方法将Service转为BoxService(实现了Clone),克隆后移入闭包:
use actix_web::dev::ServiceExt; fn call(&self, req: ServiceRequest) -> Self::Future { if ApiEnv::skip_auth() { return Box::pin(self.service.call(req)); } // 将Service转为BoxService并克隆 let service = self.service.boxed().clone(); Box::pin(async move { let auth_result = { let db: &Data<Database> = req.app_data().unwrap(); let pool = db.pool.clone(); let headers = req.headers().clone(); let mut extensions = req.extensions_mut(); match authorize(pool, headers).await { Ok(user) => { extensions.insert(user); Ok(()) } Err(e) => Err(e), } }; match auth_result { Ok(_) => service.call(req).await, Err(e) => Err(e.into()), } }) }
关键说明
两种方案都是通过转移/克隆Service的所有权,让异步闭包独立持有Service,避免生命周期绑定到&self,满足'static生命周期要求。
内容的提问来源于stack exchange,提问作者Eatenpancreas
相关产品推荐
相关产品推荐

