Trait rocket::request::FromRequest

source ·
pub trait FromRequest<'r>: Sized {
    type Error: Debug;

    // Required method
    fn from_request<'life0, 'async_trait>(
        request: &'r Request<'life0>,
    ) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'r: 'async_trait,
             'life0: 'async_trait;
}
Expand description

Trait implemented by request guards to derive a value from incoming requests.

§Request Guards

A request guard is a type that represents an arbitrary validation policy. The validation policy is implemented through FromRequest. In other words, every type that implements FromRequest is a request guard.

Request guards appear as inputs to handlers. An arbitrary number of request guards can appear as arguments in a route handler. Rocket will automatically invoke the FromRequest implementation for request guards before calling the handler. Rocket only dispatches requests to a handler when all of its guards pass.

§Async Trait

FromRequest is an async trait. Implementations of FromRequest must be decorated with an attribute of #[rocket::async_trait]:

use rocket::request::{self, Request, FromRequest};

#[rocket::async_trait]
impl<'r> FromRequest<'r> for MyType {
    type Error = MyError;

    async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
        /* .. */
    }
}

§Example

The following dummy handler makes use of three request guards, A, B, and C. An input type can be identified as a request guard if it is not named in the route attribute. This is why, for instance, param is not a request guard.

#[get("/<param>")]
fn index(param: isize, a: A, b: B, c: C) -> T { /* ... */ }

Request guards always fire in left-to-right declaration order. In the example above, the order is a followed by b followed by c. Errors are short-circuiting; if one guard errors, the remaining are not attempted.

§Outcomes

The returned Outcome of a from_request call determines how the incoming request will be processed.

  • Success(S)

    If the Outcome is Success, then the Success value will be used as the value for the corresponding parameter. As long as all other guards succeed, the request will be handled.

  • Error(Status, E)

    If the Outcome is Error, the request will fail with the given status code and error. The designated error Catcher will be used to respond to the request. Note that users can request types of Result<S, E> and Option<S> to catch Errors and retrieve the error value.

  • Forward(Status)

    If the Outcome is Forward, the request will be forwarded to the next matching route until either one succeeds or there are no further matching routes to attempt. In the latter case, the request will be sent to the Catcher for the designated Status. Note that users can request an Option<S> to catch Forwards.

§Provided Implementations

Rocket implements FromRequest for several built-in types. Their behavior is documented here.

  • Method

    Extracts the Method from the incoming request.

    This implementation always returns successfully.

  • &Origin

    Extracts the Origin URI from the incoming request.

    This implementation always returns successfully.

  • &Host

    Extracts the Host from the incoming request, if it exists. See Request::host() for details. If it does not exist, the request is forwarded with a 500 Internal Server Error status.

  • &Route

    Extracts the Route from the request if one is available. When used as a request guard in a route handler, this will always succeed. Outside of a route handler, a route may not be available, and the request is forwarded with a 500 Internal Server Error status.

    For more information on when an &Route is available, see Request::route().

  • &CookieJar

    Returns a borrow to the CookieJar in the incoming request. Note that CookieJar implements internal mutability, so a handle to a CookieJar allows you to get and set cookies in the request.

    This implementation always returns successfully.

  • &Config

    Extracts the application Config.

    This implementation always returns successfully.

  • &ContentType

    Extracts the ContentType header from the incoming request via Request::content_type(). If the request didn’t specify a Content-Type, the request is forwarded with a 500 Internal Server Error status.

  • &Accept

    Extracts the Accept header from the incoming request via Request::accept(). If the request didn’t specify an Accept, the request is forwarded with a 500 Internal Server Error status.

  • *IpAddr

    Extracts the client ip address of the incoming request as an IpAddr via Request::client_ip(). If the client’s IP address is not known, the request is forwarded with a 500 Internal Server Error status.

  • SocketAddr

    Extracts the remote address of the incoming request as a SocketAddr via Request::remote(). If the remote address is not known, the request is forwarded with a 500 Internal Server Error status.

  • Option<T> where T: FromRequest

    The type T is derived from the incoming request using T’s FromRequest implementation. If the derivation is a Success, the derived value is returned in Some. Otherwise, a None is returned.

    This implementation always returns successfully.

  • Result<T, T::Error> where T: FromRequest

    The type T is derived from the incoming request using T’s FromRequest implementation. If derivation is a Success, the value is returned in Ok. If the derivation is an Error, the error value is returned in Err. If the derivation is a Forward, the request is forwarded with the same status code as the original forward.

§Example

Imagine you’re running an authenticated API service that requires that some requests be sent along with a valid API key in a header field. You want to ensure that the handlers corresponding to these requests don’t get called unless there is an API key in the request and the key is valid. The following example implements this using an ApiKey type and a FromRequest implementation for that type. The ApiKey type is then used in the sensitive handler.

use rocket::http::Status;
use rocket::request::{self, Outcome, Request, FromRequest};

struct ApiKey<'r>(&'r str);

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

#[rocket::async_trait]
impl<'r> FromRequest<'r> for ApiKey<'r> {
    type Error = ApiKeyError;

    async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
        /// Returns true if `key` is a valid API key string.
        fn is_valid(key: &str) -> bool {
            key == "valid_api_key"
        }

        match req.headers().get_one("x-api-key") {
            None => Outcome::Error((Status::BadRequest, ApiKeyError::Missing)),
            Some(key) if is_valid(key) => Outcome::Success(ApiKey(key)),
            Some(_) => Outcome::Error((Status::BadRequest, ApiKeyError::Invalid)),
        }
    }
}

#[get("/sensitive")]
fn sensitive(key: ApiKey<'_>) -> &'static str {
    "Sensitive data."
}

§Request-Local State

Request guards that perform expensive operations, such as those that query a database or an external service, should use the request-local state cache to store results if they might be invoked multiple times during the routing of a single request.

For example, consider a pair of User and Admin guards and a pair of routes (admin_dashboard and user_dashboard):

#[rocket::async_trait]
impl<'r> FromRequest<'r> for User {
    type Error = ();

    async fn from_request(request: &'r Request<'_>) -> Outcome<User, ()> {
        let db = try_outcome!(request.guard::<Database>().await);
        request.cookies()
            .get_private("user_id")
            .and_then(|cookie| cookie.value().parse().ok())
            .and_then(|id| db.get_user(id).ok())
            .or_forward(Status::Unauthorized)
    }
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for Admin {
    type Error = ();

    async fn from_request(request: &'r Request<'_>) -> Outcome<Admin, ()> {
        // This will unconditionally query the database!
        let user = try_outcome!(request.guard::<User>().await);
        if user.is_admin {
            Outcome::Success(Admin { user })
        } else {
            Outcome::Forward(Status::Unauthorized)
        }
    }
}

#[get("/dashboard")]
fn admin_dashboard(admin: Admin) { }

#[get("/dashboard", rank = 2)]
fn user_dashboard(user: User) { }

When a non-admin user is logged in, the database will be queried twice: once via the Admin guard invoking the User guard, and a second time via the User guard directly. For cases like these, request-local state should be used, as illustrated below:

#[rocket::async_trait]
impl<'r> FromRequest<'r> for &'r User {
    type Error = std::convert::Infallible;

    async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
        // This closure will execute at most once per request, regardless of
        // the number of times the `User` guard is executed.
        let user_result = request.local_cache_async(async {
            let db = request.guard::<Database>().await.succeeded()?;
            request.cookies()
                .get_private("user_id")
                .and_then(|cookie| cookie.value().parse().ok())
                .and_then(|id| db.get_user(id).ok())
        }).await;

        user_result.as_ref().or_forward(Status::Unauthorized)
    }
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for Admin<'r> {
    type Error = std::convert::Infallible;

    async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
        let user = try_outcome!(request.guard::<&User>().await);
        if user.is_admin {
            Outcome::Success(Admin { user })
        } else {
            Outcome::Forward(Status::Unauthorized)
        }
    }
}

Notice that these request guards provide access to borrowed data (&'a User and Admin<'a>) as the data is now owned by the request’s cache.

Required Associated Types§

source

type Error: Debug

The associated error to be returned if derivation fails.

Required Methods§

source

fn from_request<'life0, 'async_trait>( request: &'r Request<'life0>, ) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>> + Send + 'async_trait>>
where Self: 'async_trait, 'r: 'async_trait, 'life0: 'async_trait,

Derives an instance of Self from the incoming request metadata.

If the derivation is successful, an outcome of Success is returned. If the derivation fails in an unrecoverable fashion, Error is returned. Forward is returned to indicate that the request should be forwarded to other matching routes, if any.

Object Safety§

This trait is not object safe.

Implementations on Foreign Types§

source§

impl<'r> FromRequest<'r> for IpAddr

source§

type Error = Infallible

source§

fn from_request<'life0, 'async_trait>( request: &'r Request<'life0>, ) -> Pin<Box<dyn Future<Output = Outcome<Self, Infallible>> + Send + 'async_trait>>
where Self: 'async_trait, 'r: 'async_trait, 'life0: 'async_trait,

source§

impl<'r> FromRequest<'r> for SocketAddr

source§

type Error = Infallible

source§

fn from_request<'life0, 'async_trait>( request: &'r Request<'life0>, ) -> Pin<Box<dyn Future<Output = Outcome<Self, Infallible>> + Send + 'async_trait>>
where Self: 'async_trait, 'r: 'async_trait, 'life0: 'async_trait,

Implementors§

source§

impl<'r> FromRequest<'r> for &'r Config

source§

impl<'r> FromRequest<'r> for &'r SecretKey

source§

impl<'r> FromRequest<'r> for &'r Limits

source§

impl<'r> FromRequest<'r> for &'r Accept

source§

impl<'r> FromRequest<'r> for &'r ContentType

source§

impl<'r> FromRequest<'r> for &'r CookieJar<'r>

source§

impl<'r> FromRequest<'r> for &'r Host<'r>

source§

impl<'r> FromRequest<'r> for &'r Origin<'r>

source§

impl<'r> FromRequest<'r> for &'r Route

source§

impl<'r> FromRequest<'r> for Method

source§

impl<'r> FromRequest<'r> for Certificate<'r>

Available on crate feature mtls only.
source§

impl<'r> FromRequest<'r> for Shutdown

source§

impl<'r> FromRequest<'r> for FlashMessage<'r>

Retrieves a flash message from a flash cookie. If there is no flash cookie, or if the flash cookie is malformed, an empty Err is returned.

The suggested use is through an Option and the FlashMessage type alias in request: Option<FlashMessage>.

source§

impl<'r, T: Send + Sync + 'static> FromRequest<'r> for &'r State<T>

source§

impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T>

source§

impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error>

source§

impl<'r, T: FromRequest<'r>> FromRequest<'r> for Outcome<T, T::Error>