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
isSuccess
, then theSuccess
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
isError
, the request will fail with the given status code and error. The designated errorCatcher
will be used to respond to the request. Note that users can request types ofResult<S, E>
andOption<S>
to catchError
s and retrieve the error value. -
Forward(Status)
If the
Outcome
isForward
, 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 theCatcher
for the designatedStatus
. Note that users can request anOption<S>
to catchForward
s.
§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. SeeRequest::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, seeRequest::route()
. -
&CookieJar
Returns a borrow to the
CookieJar
in the incoming request. Note thatCookieJar
implements internal mutability, so a handle to aCookieJar
allows you to get and set cookies in the request.This implementation always returns successfully.
-
Extracts the application
Config
.This implementation always returns successfully.
-
&ContentType
Extracts the
ContentType
header from the incoming request viaRequest::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 viaRequest::accept()
. If the request didn’t specify anAccept
, the request is forwarded with a 500 Internal Server Error status. -
*IpAddr
Extracts the client ip address of the incoming request as an
IpAddr
viaRequest::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
viaRequest::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 usingT
’sFromRequest
implementation. If the derivation is aSuccess
, the derived value is returned inSome
. Otherwise, aNone
is returned.This implementation always returns successfully.
-
Result<T, T::Error> where T: FromRequest
The type
T
is derived from the incoming request usingT
’sFromRequest
implementation. If derivation is aSuccess
, the value is returned inOk
. If the derivation is anError
, the error value is returned inErr
. If the derivation is aForward
, 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§
Required Methods§
sourcefn 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,
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§
Implementations on Foreign Types§
source§impl<'r> FromRequest<'r> for IpAddr
impl<'r> FromRequest<'r> for IpAddr
type Error = Infallible
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
impl<'r> FromRequest<'r> for SocketAddr
type Error = Infallible
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
impl<'r> FromRequest<'r> for &'r Config
type Error = Infallible
source§impl<'r> FromRequest<'r> for &'r SecretKey
impl<'r> FromRequest<'r> for &'r SecretKey
type Error = Infallible
source§impl<'r> FromRequest<'r> for &'r Limits
impl<'r> FromRequest<'r> for &'r Limits
type Error = Infallible
source§impl<'r> FromRequest<'r> for &'r Accept
impl<'r> FromRequest<'r> for &'r Accept
type Error = Infallible
source§impl<'r> FromRequest<'r> for &'r ContentType
impl<'r> FromRequest<'r> for &'r ContentType
type Error = Infallible
source§impl<'r> FromRequest<'r> for &'r CookieJar<'r>
impl<'r> FromRequest<'r> for &'r CookieJar<'r>
type Error = Infallible
source§impl<'r> FromRequest<'r> for &'r Host<'r>
impl<'r> FromRequest<'r> for &'r Host<'r>
type Error = Infallible
source§impl<'r> FromRequest<'r> for &'r Origin<'r>
impl<'r> FromRequest<'r> for &'r Origin<'r>
type Error = Infallible
source§impl<'r> FromRequest<'r> for &'r Route
impl<'r> FromRequest<'r> for &'r Route
type Error = Infallible
source§impl<'r> FromRequest<'r> for Method
impl<'r> FromRequest<'r> for Method
type Error = Infallible
source§impl<'r> FromRequest<'r> for Certificate<'r>
Available on crate feature mtls
only.
impl<'r> FromRequest<'r> for Certificate<'r>
mtls
only.source§impl<'r> FromRequest<'r> for Shutdown
impl<'r> FromRequest<'r> for Shutdown
type Error = Infallible
source§impl<'r> FromRequest<'r> for FlashMessage<'r>
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>
.