如何为 Rocket.rs 设置 CORS 或 OPTIONS

How to set up CORS or OPTIONS for Rocket.rs

我有一个后端 运行 rocket.rs,我的 flutter web 应用程序向其发送请求,但它无法通过 OPTIONS 响应。

我已经尝试将 CORS (rocket_cors) 添加到后端并有一个选项响应,但它仍然发回:

Error: XMLHttpRequest error.
    dart:sdk_internal 124039:30                           get current
packages/http/src/browser_client.dart.lib.js 214:124  <fn>

我已将以下内容添加到我的火箭项目中:

#[options("/")]
fn send_options<'a>(path: PathBuf) -> Response<'a> {
    let mut res = Response::new();
    res.set_status(Status::new(200, "No Content"));
    res.adjoin_header(ContentType::Plain);
    res.adjoin_raw_header("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
    res.adjoin_raw_header("Access-Control-Allow-Origin", "*");
    res.adjoin_raw_header("Access-Control-Allow-Credentials", "true");
    res.adjoin_raw_header("Access-Control-Allow-Headers", "Content-Type");
    res

我的 flutter 应用是 运行 这个请求:

Future<String> fetchData() async {
  final data2 = await http.get("http://my-web-site.com").then((response) { // doesn't get past here
    return response.body; 
  });
  return data2;
}

问题:这是响应 OPTION 请求的正确方法吗?如果不是,我如何在 rocket.rs 中实现它?

Lambda Fairy 的评论帮我解答了。
我把它全部放在 GET 处理程序中:

#[get("/")]
fn get_handler<'a>() -> Response<'a> {
    let mut res = Response::new();
    res.set_status(Status::new(200, "No Content"));
    res.adjoin_header(ContentType::Plain);
    res.adjoin_raw_header("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
    res.adjoin_raw_header("Access-Control-Allow-Origin", "*");
    res.adjoin_raw_header("Access-Control-Allow-Credentials", "true");
    res.adjoin_raw_header("Access-Control-Allow-Headers", "Content-Type");
    res.set_sized_body(Cursor::new("Response")); 
    res

为了让服务器提供外部 API 它需要能够处理跨源资源共享 (CORS)。 CORS 是一种基于 HTTP-header 的机制,它允许服务器指示浏览器应允许加载资源的来源(域、协议或端口)。

您可以创建一个整流罩来为您的应用全局处理 CORS。一个非常宽松的版本如下,但当然,您必须根据您的特定应用程序进行定制。

火箭 0.4

use rocket::http::Header;
use rocket::{Request, Response};
use rocket::fairing::{Fairing, Info, Kind};

pub struct CORS;

impl Fairing for CORS {
    fn info(&self) -> Info {
        Info {
            name: "Add CORS headers to responses",
            kind: Kind::Response
        }
    }

    fn on_response(&self, request: &Request, response: &mut Response) {
        response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
        response.set_header(Header::new("Access-Control-Allow-Methods", "POST, GET, PATCH, OPTIONS"));
        response.set_header(Header::new("Access-Control-Allow-Headers", "*"));
        response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
    }
}

火箭 0.5

use rocket::http::Header;
use rocket::{Request, Response};
use rocket::fairing::{Fairing, Info, Kind};

pub struct CORS;

#[rocket::async_trait]
impl Fairing for CORS {
    fn info(&self) -> Info {
        Info {
            name: "Add CORS headers to responses",
            kind: Kind::Response
        }
    }

    async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
        response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
        response.set_header(Header::new("Access-Control-Allow-Methods", "POST, GET, PATCH, OPTIONS"));
        response.set_header(Header::new("Access-Control-Allow-Headers", "*"));
        response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
    }
}

您只需像这样安装整流罩:

rocket::ignite().attach(CORS)

或者,您可以使用 rocket_cors 箱子。

use rocket::http::Method;
use rocket_cors::{AllowedOrigins, CorsOptions};

let cors = CorsOptions::default()
    .allowed_origins(AllowedOrigins::all())
    .allowed_methods(
        vec![Method::Get, Method::Post, Method::Patch]
            .into_iter()
            .map(From::from)
            .collect(),
    )
    .allow_credentials(true);

rocket::ignite().attach(cors.to_cors().unwrap())

您可以了解有关 CORS 和访问控制的更多信息 headers here

这对我有用:

use rocket::http::Header;
use rocket::{Request, Response};
use rocket::fairing::{Fairing, Info, Kind};

pub struct CORS;

#[rocket::async_trait]
impl Fairing for CORS {
    fn info(&self) -> Info {
        Info {
            name: "Attaching CORS headers to responses",
            kind: Kind::Response
        }
    }

    async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
        response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
        response.set_header(Header::new("Access-Control-Allow-Methods", "POST, GET, PATCH, OPTIONS"));
        response.set_header(Header::new("Access-Control-Allow-Headers", "*"));
        response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
    }
}

并且您必须使用启动宏将其附加到函数中:

#[launch]
fn rocket() -> _ {
    rocket::build()
        .attach(CORS)
        .mount("/index", routes![index])
}

要获得跨源资源共享支持,您必须拦截响应 由您的 Rocket 服务器发送。你想实现一个中间件来实现它, 在 Rocket 上,你必须在 struct 上实现 Fairing 特性才能 做到这一点。

Rocket v0.5.x(不稳定)

如果您在 Rocket 的文档中搜索版本 0。5.x。

Trait implemented by fairings: Rocket’s structured middleware.

Source

You must decorate the Fairing trait implementation with the rocket::async-trait attribute.

use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::Header;
use rocket::{Request, Response};

pub struct Cors;

#[rocket::async_trait]
impl Fairing for Cors {
    fn info(&self) -> Info {
        Info {
            name: "Cross-Origin-Resource-Sharing Middleware",
            kind: Kind::Response,
        }
    }

    async fn on_response<'r>(&self,
        request: &'r Request<'_>,
        response: &mut Response<'r>) {
        response.set_header(Header::new(
            "access-control-allow-origin",
            "https://example.com",
        ));
        response.set_header(Header::new(
            "access-control-allow-methods",
            "GET, PATCH, OPTIONS",
        ));
    }
}

在您的 main.rs 文件中,您必须附加中间件:

mod config;
mod middleware;
mod routes;

use self::config::Config;

#[macro_use]
extern crate rocket;

#[launch]
async fn rocket() -> _ {
    let config = Config::new();

    rocket::custom(&config.server_config)
        .attach(middleware::cors::Cors)
        .mount(
            "/api/v1",
            routes![routes::index],
        )
}

火箭 0.4.x(稳定)

参考

万一有人找火箭>=rc5.0

use rocket::http::Header;
use rocket::{Request, Response};
use rocket::fairing::{Fairing, Info, Kind};

pub struct CORS;

#[rocket::async_trait]
impl Fairing for CORS {
    fn info(&self) -> Info {
        Info {
            name: "Add CORS headers to responses",
            kind: Kind::Response
       }
    }


    async fn on_response<'r>(&self, req: &'r Request<'_>, response: &mut Response<'r>) {
        response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
        response.set_header(Header::new("Access-Control-Allow-Methods", "POST, GET, PATCH, OPTIONS"));
        response.set_header(Header::new("Access-Control-Allow-Headers", "*"));
        response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
    }
}

耶稣。几个小时后,我终于让它工作了。首先,一个小警告:我们正在处理高度不稳定的代码,这个答案在您阅读时可能已经过时。 Ibraheem Ahmeed 的回答中 matthewscottgordon 的编辑为我指明了正确的方向。

我们将使用 rocket_cors 箱子。

1。安装旧版本的 Rust Nightly。截至 1 月 22 日,Rust Nightly V 1.6.0 中断 mongodb 2.1.0.

执行此操作以防最新的每晚未编译。

您可以按以下方式执行此操作:

rustup override set nightly-2021-11-10 This will download 1.58 nightly.

2。添加 rocket_cors 依赖项。截至目前,主版本有 1.6.0 版本,目标是 rocket 的 0.5.0-rc.1 版本

rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", branch = "master" }

这是我的 cargo.toml:


[dependencies]
rocket = {version ="0.5.0-rc.1", features=["json"]}
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", branch = "master" }
reqwest = {version = "0.11.6", features = ["json"] }
serde= {version = "1.0.117", features= ["derive"]}
mongodb = "2.1.0"
rand = "0.8.4"

3。将以下内容添加到您的主要 Rocket 函数中:

extern crate rocket;

use std::error::Error;
use rocket_cors::{AllowedOrigins, CorsOptions};

#[rocket::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let cors = CorsOptions::default()
        .allowed_origins(AllowedOrigins::all())
        .allowed_methods(
            vec![Method::Get, Method::Post, Method::Patch]
                .into_iter()
                .map(From::from)
                .collect(),
        )
        .allow_credentials(true)
        .to_cors()?;

    // let path = concat!(env!("CARGO_MANIFEST_DIR"), "/public");
    rocket::build()
        .mount("/", routes![index, upload::upload, post_favorites])
        .attach(cors)
        .launch()
        .await?;


    Ok(())
}

如果这个答案对您不起作用,请务必查看rocket_cors repository for up to date examples. The example above was used with the fairing.rs文件

查看存储库的 Cargo.toml 文件。

要检查 let cors 中的默认值,(如果使用 VS Code 和 Rust 扩展)将鼠标悬停在 CorsOptions 上。