Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 305
Will Avatar asked Jun 16 '20 15:06

Will


2 Answers

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

like image 112
Ibraheem Ahmed Avatar answered Sep 28 '22 15:09

Ibraheem Ahmed


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])
}
like image 26
José Alvarez Avatar answered Sep 28 '22 13:09

José Alvarez