You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

actix-web后端CORS策略拦截请求问题排查求助

Actix-Web中Cors::permissive()未生效,部分路由跨域被拦截的问题分析与解决

问题根源

核心问题出在中间件执行顺序和预请求拦截上:

  • Actix-Web的中间件按添加顺序执行,你虽然加了Cors::permissive(),但AuthMiddleware在它之后处理请求。浏览器发送的预请求(OPTIONS)不会携带Authorization头,AuthMiddleware会因为缺少token直接返回错误响应,此时Cors中间件还没来得及添加跨域头,导致浏览器报错。
  • 登录/注册路由不需要经过AuthMiddleware验证(或你的中间件对这两个路由做了放行),所以它们的预请求能正常被Cors处理,返回正确的跨域头,因此可以正常访问。

解决方案

方案1:调整中间件顺序,让Cors最先执行

把Cors中间件放在所有中间件最前面,确保所有请求(包括预请求)先经过跨域处理,再进入其他中间件:

#[actix_web::main]
async fn main() -> std::io::Result<()> {
   dotenv().ok();
   std::env::set_var("RUST_LOG", "actix_web=info");
   env_logger::init();
   
   let pool: Pool<Postgres> = PgPoolOptions::new()
       .max_connections(5)
       .connect(&std::env::var("LOCAL_DATABASE_URL").expect("DATABASE_URL must be set"))
       .await
       .expect("Error building a connection pool");

       HttpServer::new(move || {
           App::new()
               .wrap(Cors::permissive()) // 移到最前面
               .wrap(Logger::default())
               .wrap(AuthMiddleware)
               .app_data(Data::new(AppState{db:pool.clone()}))
               .route("/signup", web::post().to(signup))
               .route("/login", web::post().to(login))
               .route("/recruit", web::post().to(recruit))
               .route("/get_recruiter", web::post().to(recruiter))
   })
   .bind("127.0.0.1:8080")?
   .workers(10)
   .run()
   .await
}

方案2:让AuthMiddleware跳过OPTIONS请求

如果不想调整中间件顺序,可修改AuthMiddleware,直接放行预请求(OPTIONS)——这类请求不需要token验证:

// 假设你的AuthMiddleware实现如下,添加OPTIONS请求判断
impl<S, B> Transform<S, ServiceRequest> for AuthMiddleware
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = AuthMiddlewareService<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(AuthMiddlewareService { service })
    }
}

pub struct AuthMiddlewareService<S> {
    service: S,
}

impl<S, B> Service<ServiceRequest> for AuthMiddlewareService<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

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

    fn call(&self, req: ServiceRequest) -> Self::Future {
        // 直接放行OPTIONS预请求
        if req.method() == Method::OPTIONS {
            return Box::pin(async move { self.service.call(req).await });
        }

        // 你的原有token验证逻辑...
        let fut = self.service.call(req);
        Box::pin(async move {
            let res = fut.await?;
            Ok(res)
        })
    }
}

验证方法

修改后可以用curl测试预请求是否返回正确的跨域头:

curl -X OPTIONS http://127.0.0.1:8080/get_recruiter -H "Origin: http://10.0.0.77:3000" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: Authorization" -v

如果响应头中包含Access-Control-Allow-Origin: *,说明Cors配置已生效。

内容的提问来源于stack exchange,提问作者Santeau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:32:42