[][src]Trait rocket::request::FromRequest

pub trait FromRequest<'a, 'r>: Sized {
    type Error: Debug;
    fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error>;
}

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.

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. Failure is short-circuiting; if one guard fails, the remaining are not attempted.

Outcomes

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

Provided Implementations

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

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::Outcome;
use rocket::http::Status;
use rocket::request::{self, Request, FromRequest};

struct ApiKey(String);

/// Returns true if `key` is a valid API key string.
fn is_valid(key: &str) -> bool {
    key == "valid_api_key"
}

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

impl<'a, 'r> FromRequest<'a, 'r> for ApiKey {
    type Error = ApiKeyError;

    fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
        let keys: Vec<_> = request.headers().get("x-api-key").collect();
        match keys.len() {
            0 => Outcome::Failure((Status::BadRequest, ApiKeyError::Missing)),
            1 if is_valid(keys[0]) => Outcome::Success(ApiKey(keys[0].to_string())),
            1 => Outcome::Failure((Status::BadRequest, ApiKeyError::Invalid)),
            _ => Outcome::Failure((Status::BadRequest, ApiKeyError::BadCount)),
        }
    }
}

#[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):

impl<'a, 'r> FromRequest<'a, 'r> for User {
    type Error = ();

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

impl<'a, 'r> FromRequest<'a, 'r> for Admin {
    type Error = ();

    fn from_request(request: &'a Request<'r>) -> request::Outcome<Admin, ()> {
        // This will unconditionally query the database!
        let user = request.guard::<User>()?;

        if user.is_admin {
            Outcome::Success(Admin { user })
        } else {
            Outcome::Forward(())
        }
    }
}

#[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:

impl<'a, 'r> FromRequest<'a, 'r> for &'a User {
    type Error = !;

    fn from_request(request: &'a Request<'r>) -> request::Outcome<&'a User, !> {
        // 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(|| {
            let db = request.guard::<Database>().succeeded()?;
            request.cookies()
                .get_private("user_id")
                .and_then(|cookie| cookie.value().parse().ok())
                .and_then(|id| db.get_user(id).ok())
        });

        user_result.as_ref().or_forward(())
    }
}

impl<'a, 'r> FromRequest<'a, 'r> for Admin<'a> {
    type Error = !;

    fn from_request(request: &'a Request<'r>) -> request::Outcome<Admin<'a>, !> {
        let user = request.guard::<&User>()?;

        if user.is_admin {
            Outcome::Success(Admin { user })
        } else {
            Outcome::Forward(())
        }
    }
}

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.

Associated Types

type Error: Debug

The associated error to be returned if derivation fails.

Loading content...

Required methods

fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error>

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, Failure is returned. Forward is returned to indicate that the request should be forwarded to other matching routes, if any.

Loading content...

Implementations on Foreign Types

impl<'a, 'r> FromRequest<'a, 'r> for SocketAddr[src]

type Error = !

impl<'a, 'r, T: FromRequest<'a, 'r>> FromRequest<'a, 'r> for Result<T, T::Error>[src]

type Error = !

impl<'a, 'r, T: FromRequest<'a, 'r>> FromRequest<'a, 'r> for Option<T>[src]

type Error = !

Loading content...

Implementors

impl<'a, 'r> FromRequest<'a, 'r> for &'a Accept[src]

type Error = !

impl<'a, 'r> FromRequest<'a, 'r> for &'a ContentType[src]

type Error = !

impl<'a, 'r> FromRequest<'a, 'r> for &'a Origin<'a>[src]

type Error = !

impl<'a, 'r> FromRequest<'a, 'r> for &'r Route[src]

type Error = !

impl<'a, 'r> FromRequest<'a, 'r> for Cookies<'a>[src]

type Error = !

impl<'a, 'r> FromRequest<'a, 'r> for Method[src]

type Error = !

impl<'a, 'r> FromRequest<'a, 'r> for Flash<&'a Request<'r>>[src]

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>.

type Error = ()

impl<'a, 'r, T: Send + Sync + 'static> FromRequest<'a, 'r> for State<'r, T>[src]

type Error = ()

Loading content...