React调用Actix API遇CORS错误:缺少Access-Control-Allow-Origin头
问题描述
React调用Actix API时触发CORS错误,提示请求资源不存在Access-Control-Allow-Origin头。移除请求中的Authorization头后API可正常响应,手动在Actix返回中添加该头也无效。
完整错误信息:
Access to fetch at 'https://exampleip/richieste' from origin 'https://exampleip' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
相关代码
React请求代码
async function richiesteFn() { try { const response = await fetch('https://exampleip/richieste', { method: 'GET', headers: { Authorization: sessionStorage.getItem('auth0_token'), }, }); const data = await response.json(); // console.log(data); return data; } catch (error) { return error; } }
Actix API接口代码
#[get("/richieste")] async fn richieste_srvc(req: HttpRequest) -> HttpResponse { let authorization_present = req.headers().contains_key("Authorization"); if authorization_present { let authorization = req.headers().get("Authorization").unwrap(); let auth_str = authorization.to_str().unwrap(); let validate_token_fn = validate_auth0_token(&auth_str); if validate_token_fn.is_err(){ let error = validate_token_fn.unwrap_err(); let err_str = format!("{{ \"Error\": \"{}\" }}", error); HttpResponse::Ok().body(err_str) } else { let str = mostra_richieste(); if str.is_err() { let error = str.unwrap_err(); let err_str = format!("{{ \"Error\": \"Errore nella lista richieste: {}\" }}", error); HttpResponse::Ok().content_type(ContentType::json()).body(err_str) } else { HttpResponse::Ok().content_type(ContentType::json()).body(str.unwrap()) } } } else { let err_str = format!("{{ \"Error\": \"Authorization header not present\" }}"); HttpResponse::Ok().content_type(ContentType::json()).body(err_str) } }
Actix服务启动代码
HttpServer::new(move || { let logger = Logger::default(); let cors_allowed_origin = env::var("CORS_ALLOWED_ORIGIN").unwrap(); let cors = Cors::default() .allowed_origin(&cors_allowed_origin); App::new() .wrap(logger) .wrap(cors) .service(login) .service(validate_token) .service(richieste_srvc) })
解决方案
问题核心是预检请求(OPTIONS)未被正确处理:当请求包含Authorization这类非简单头时,浏览器会先发送OPTIONS预检请求验证权限,你的CORS配置未允许自定义头和OPTIONS方法,导致预检失败。
更新CORS配置,允许自定义头与预检方法
修改Actix启动代码中的CORS配置,添加对Authorization头和OPTIONS方法的支持:let cors = Cors::default() .allowed_origin(&cors_allowed_origin) .allowed_header("Authorization") // 显式允许Authorization头 .allowed_methods(vec!["GET", "OPTIONS"]) // 允许请求方法,包含预检用的OPTIONS .supports_credentials(); // 若请求需要携带凭证(如cookie),保留此行,否则可移除调整中间件顺序
确保CORS中间件优先于Logger执行,因为预检请求需要先被CORS中间件处理:App::new() .wrap(cors) // 先加载CORS中间件 .wrap(logger) .service(login) .service(validate_token) .service(richieste_srvc)校验环境变量值
确认CORS_ALLOWED_ORIGIN环境变量的值与React应用的Origin完全一致,不要带末尾斜杠(如正确值为https://exampleip,而非https://exampleip/)。禁止手动添加CORS头
不要在接口的HttpResponse中手动添加Access-Control-Allow-Origin头,Actix的CORS中间件会自动处理,手动添加可能导致头冲突。
内容的提问来源于stack exchange,提问作者GranBoh

