How to retrieve HTTP headers from a request in Rocket?

Viewed 2739

I would do something like this using flask in Python:

@app.route('/login/', methods=['POST'])
def login():
    token = request.headers["token"]

I cannot figure out how to access the token header and store it as a String variable.

#![feature(proc_macro_hygiene, decl_macro)]

use rocket::{
    config::{Config, Environment},
    *,
};

fn main() {
    let config = Config::build(Environment::Production)
        .address("0.0.0.0")
        .port(PORT)
        .finalize()
        .unwrap();

    rocket::ignite().mount("/", routes![login]).launch();
}

#[post("/login")]
fn login() {

    // Retrieve headers from request.
}
2 Answers

Ibraheem Ahmed's answer was useful, but I could not figure out how to use Infallible. I solved the issue by doing:

struct Token(String);

#[derive(Debug)]
enum ApiTokenError {
    Missing,
    Invalid,
}

impl<'a, 'r> FromRequest<'a, 'r> for Token {
    type Error = ApiTokenError;

    fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
        let token = request.headers().get_one("token");
        match token {
            Some(token) => {
                // check validity
                Outcome::Success(Token(token.to_string()))
            }
            None => Outcome::Failure((Status::Unauthorized, ApiTokenError::Missing)),
        }
    }
}

Rocket handlers are based on Request Guards. You do not directly access the request in your handler. Instead, you create a type that implements FromRequest.

You can create a token struct that holds a string:

struct Token(String);

And implement FromRequest for the token:

impl<'a, 'r> FromRequest<'a, 'r> for Token {
    type Error = Infallible;

    fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
        let token = request.headers().get_one("token");
        match token {
          Some(token) => {
            // check validity
            Outcome::Success(Token(token.to_string()))
          },
          // token does not exist
          None => Outcome::Failure(Status::Unauthorized)
        }
    }
}

Now you can use that Token as a request guard:

#[post("/login")]
fn login(token: Token) {
}

If the from_request method for Token fails, a Status::Unauthorized will be returned. Otherwise, your handler will be called, and you can handle the authentication logic.

Related