Search code examples
rustcorsrust-rocket

Rocket CORS how to return string with Request Guard?


I have a rocket (0.5.0-rc.1) route, that returns a content::Json<String> and I would like to add CORS to that route using rocket_cors (from master).

Specifically I want to use the RequestGuard, because I only want to enable CORS for certain routes.

My original request looked like this:

#[get("/json")]
fn json_without_cors() -> content::Json<String> {
    let test = Test {
        field1: 0,
        field2: String::from("Test"),
    };
    let json = serde_json::to_string(&test).expect("Failed to encode data.");

    content::Json(json)
}

and I changed it to use CORS (based on this example) like so

#[get("/json")]
fn json(cors: Guard<'_>) -> Responder<'_, '_, content::Json<String>> {
    let test = Test {
        field1: 0,
        field2: String::from("Test"),
    };
    let json = serde_json::to_string(&test).expect("Failed to encode data.");

    cors.responder(content::Json(json))
}

Unfortunately this now fails to compile with:

error[E0621]: explicit lifetime required in the type of `cors`
  --> src/main.rs:35:10
   |
28 | fn json(cors: Guard<'_>) -> Responder<'_, '_, content::Json<String>> {
   |               --------- help: add explicit lifetime `'static` to the type of `cors`: `Guard<'static>`
...
35 |     cors.responder(content::Json(json))
   |          ^^^^^^^^^ lifetime `'static` required

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0621, E0759.
For more information about an error, try `rustc --explain E0621`.
error: could not compile `cors_json`

I can't give Guard a 'static lifetime, because this leads to further issues down the road.

How can I return a content::Json<String> from my request with CORS?

A full example can be found on Github.


Solution

  • It's due to the fact that rocket_cors has lifetime bounds on the Responder struct, and these make the struct covariant on these lifetime bounds (so it makes it reject a 'static lifetime when it shouldn't).

    The good news is that these bounds are not necessary on the struct's declaration since they can exist solely on the related impl blocks.

    I've created a pull request, but this will be a breaking API change since Responder will no longer be directly generic over these lifetimes. If you want to keep tracking their master branch, you can do what @Hadus suggested and pass a 'static as the Responder's lifetime param.

    Using the PR's branch you can directly do:

    #[get("/json")]
    fn json(cors: Guard<'_>) -> Responder<content::Json<String>> {
        let test = Test {
            field1: 0,
            field2: String::from("Test"),
        };
        let json = serde_json::to_string(&test).expect("Failed to encode data.");
    
        cors.responder(content::Json(json))
    }
    

    Update: It's been merged.