'How to set up CORS or OPTIONS for Rocket.rs
I've got a backend running rocket.rs which my flutter web app sends a request to, but it can't get past the OPTIONS response.
I have tried adding CORS (rocket_cors) to the backend and having a options response, but it still sends back:
Error: XMLHttpRequest error.
dart:sdk_internal 124039:30 get current
packages/http/src/browser_client.dart.lib.js 214:124 <fn>
I have added the following to my rocket project:
#[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
And my flutter app is running this request:
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;
}
Question: Is this the proper way to respond to OPTION requests, and if not, how can I implement it in rocket.rs?
Solution 1:[1]
In order for a server to provide an external API it needs to be able to deal with Cross Origin Resource Sharing (CORS). CORS is an HTTP-header based mechanism that allows a server to indicate which origins (domain, protocol, or port) that a browser should permit loading of resources.
You can create a fairing to handle CORS globally for your app. A very permissive version would be as follows, but of course, you'll have to tailor to your specific application.
Rocket 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"));
}
}
Rocket 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"));
}
}
You just have to attach the fairing like this:
rocket::ignite().attach(CORS)
Alternatively, you can use the rocket_cors crate.
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())
You can learn more about CORS and Access Control headers here
Solution 2:[2]
This did the trick for me:
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"));
}
}
And you have to attach it in the function with the launch macro:
#[launch]
fn rocket() -> _ {
rocket::build()
.attach(CORS)
.mount("/index", routes![index])
}
Solution 3:[3]
Lambda Fairy's comment answered it for me.
I put it all in the GET Handler:
#[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
Solution 4:[4]
in case someone is looking for rocket>= 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"));
}
}
Solution 5:[5]
To have Cross Origin Resource Sharing support you must intercept responses
sent by your Rocket server. You want to implement a middleware to achieve that,
on Rocket you must implement the Fairing trait on a struct in order to
achieve that.
Rocket v0.5.x (Not stable)
If you on search Rocket's documentation for version 0.5.x.
Trait implemented by fairings: Rocket’s structured middleware.
You must decorate the
Fairingtraitimplementation 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",
));
}
}
On your main.rs file, you must attach the middleware:
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],
)
}
Rocket 0.4.x (Stable)
Refer to Ibraheem Ahmed answer
Solution 6:[6]
Jesus. After several hours I finally got it working. First and foremost, a small warning: We are working with highly unstable code and this answer may be obsolete by the time you're reading. The edit from matthewscottgordon in Ibraheem Ahmeed's answer pointed me to the right direction.
We're going to be using the rocket_cors crate.
1. Install an old version of Rust Nightly. As of Jan 22nd, Rust Nightly V 1.6.0 breaks mongodb 2.1.0.
Do this just in case the latest nightly isn't compiling.
You can do this in the following fashion:
rustup override set nightly-2021-11-10 This will download 1.58 nightly.
2. Add the rocket_cors dependency. As of now, the master version has a 1.6.0 release, which targets rocket's 0.5.0-rc.1 release
rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", branch = "master" }
This is my 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. Add the following to your main Rocket function:
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(())
}
If by any chance this answer doesn't work for you, be sure to check out rocket_cors repository for up to date examples. The example above was used with the fairing.rs file
Check out the repo's Cargo.toml file.
To check for the default values in let cors, (if using VS Code, and the Rust extension) hover over CorsOptions with your mouse.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | José Alvarez |
| Solution 3 | Will |
| Solution 4 | RyanShao |
| Solution 5 | |
| Solution 6 | Jose A |
