pub trait FromRequest: Sized {
type Context: RequestContext;
type Future: Future<Item = Self, Error = BoxedError> + Send;
// Required method
fn from_request_and_body(
request: &Arc<Request<()>>,
body: Body,
context: Self::Context,
) -> Self::Future;
// Provided methods
fn from_request(
request: Request<Body>,
context: Self::Context,
) -> Self::Future { ... }
fn from_request_sync(
request: Request<Body>,
context: Self::Context,
) -> Result<Self, BoxedError> { ... }
}Expand description
Trait for asynchronous conversion from HTTP requests.
§#[derive(FromRequest)]
This trait can be derived for enums to generate a request router and decoder. Here’s a simple example:
use hyperdrive::{FromRequest, body::Json};
#[derive(FromRequest)]
enum Routes {
#[get("/")]
Index,
#[get("/users/{id}")]
User { id: u32 },
#[post("/login")]
Login {
#[body]
data: Json<Login>,
},
}
#[derive(Deserialize)]
struct Login {
email: String,
password: String,
}Calling Routes::from_request will result in Routes::Index for a GET /
request, and in Routes::User for a GET /users/123 request, for example.
A POST /login request will end up as Routes::Login, decoding the POSTed
JSON body.
The generated FromRequest implementation will always use
DefaultFuture<Self, BoxedError> as the associated
Result type.
Note that the generated implementation will make use of .and_then() to
chain asynchronous operations instead of running them in parallel using
join_all. This is because it simplifies the code and doesn’t require
making use of boxed futures everywhere in the generated code. Multiple
requests will still be handled in parallel, so this should not negatively
affect performance.
In order to keep the implementation simple and user code more easily understandable, overlapping paths are not allowed (unless the paths are exactly the same, and the method differs), so the following will fail to compile:
use from_request::{FromRequest, body::Json};
#[derive(FromRequest)] //~ ERROR: route `#[get("/users/me")]` overlaps with ...
enum Routes {
#[get("/users/{id}")]
User { id: u32 },
#[get("/users/me")]
Me,
}To fix this, you can define a custom type implementing FromStr and use
that:
use hyperdrive::FromRequest;
#[derive(FromRequest)]
enum Routes {
#[get("/users/{id}")]
User { id: UserId },
}
enum UserId {
/// User by database ID.
Id(u32),
/// The currently logged-in user.
Me,
}
impl FromStr for UserId {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "me" {
Ok(UserId::Me)
} else {
Ok(UserId::Id(s.parse()?))
}
}
}§Implicit HEAD routes
The custom derive will create a HEAD route for every defined GET route,
unless you define one yourself. If your app uses AsyncService or
SyncService, those adapters will automatically take care of dropping the
body from the response to HEAD requests. If you manually call
FromRequest::from_request, you have to make sure no body
is sent back for HEAD requests.
§Extracting Request Data
The custom derive provides easy access to various kinds of data encoded in a request:
- The Request path (
/users/or/other/stuff) - Query parameters (
?name=val) - The request body
§Extracting Path Segments ({field} syntax)
In a route attribute, the {field} placeholder syntax will match a path
segment and convert it to the type of the named field using FromStr:
#[get("/users/{id}")]To extract multiple path segments this way, the {field...} syntax can be
used at the end of the path, which will consume the rest of the path:
#[get("/static/{path...}")]If the FromStr conversion fails, the generated FromRequest
implementation will bail out with an error (in other words, this feature
cannot be used to try multiple routes in sequence until one matches).
§Extracting the request body (#[body] attribute)
Putting #[body] on a field of a variant will deserialize the request body
using the FromBody trait and store the result in the annotated field:
#[post("/login")]
Login {
#[body]
data: Json<Login>,
},The type of the field must implement FromBody. The body module
contains predefined adapters implementing that trait, which work with any
type implementing Deserialize.
§Extracting query parameters (#[query_params] attribute)
The route attribute cannot match or extract query parameters (?name=val).
Instead, query parameters can be extracted by marking a field in the struct
with the #[query_params] attribute:
use hyperdrive::FromRequest;
#[derive(FromRequest)]
enum Routes {
#[get("/users")]
UserList {
#[query_params]
pagination: Option<Pagination>,
},
}
#[derive(Deserialize)]
struct Pagination {
start_id: u32,
count: u32,
}A request like GET /users?start_id=42&count=10 would thus end up with a
corresponding Pagination object, while GET /users would store None in
the pagination field.
The type of the #[query_params] field must implement serde’s Deserialize
trait and the conversion will be performed using the serde_urlencoded
crate.
§Guards
Guards can be used to prevent a route from being called when a condition is not fulfilled (for example, when the user isn’t logged in). They can also extract arbitrary data from the request headers (eg. a session ID, or the User-Agent string).
All fields that are neither mentioned in the route path nor annotated with
an attribute are considered guards and thus must implement the Guard
trait.
use hyperdrive::{FromRequest, Guard};
struct User {
id: u32,
// ...
}
impl Guard for User {
// (omitted for brevity)
}
#[derive(FromRequest)]
enum Route {
#[get("/login")]
LoginForm,
#[get("/staff")]
Staff {
// Require a logged-in user to make this request
user: User,
},
}§Forwarding
A field whose type implements FromRequest can be marked with #[forward].
The library will then generate code that invokes this nested FromRequest
implementation.
This feature can not be combined with #[body] inside the same variant,
since both consume the request body.
Currently, this is limited to FromRequest implementations that use the
same RequestContext as the outer type (ie. no automatic AsRef
conversion will take place).
A variant or struct defining a #[forward] field does not have to define
a route. If no other route matches, this variant will automatically be
created, and is considered a fallback route.
Combined with generics, this feature can be used to make request wrappers
that attach a guard or a guard group to any type implementing FromRequest:
use hyperdrive::{FromRequest, Guard};
struct User;
impl Guard for User {
// (omitted for brevity)
}
#[derive(FromRequest)]
struct Authenticated<T> {
user: User,
#[forward]
inner: T,
}§Changing the Context type
By default, the generated code will use NoContext as the associated
Context type. You can change this to any other type that implements
RequestContext by putting a #[context(MyContext)] attribute on the
type:
use hyperdrive::{FromRequest, RequestContext};
#[derive(RequestContext)]
struct MyContext {
db: MyDatabaseConnection,
}
#[derive(FromRequest)]
#[context(MyContext)]
enum Routes {
#[get("/users")]
UserList,
}For more info on this, refer to the RequestContext trait.
Required Associated Types§
Sourcetype Context: RequestContext
type Context: RequestContext
A context parameter passed to from_request.
This can be used to pass application-specific data like a logger or a database connection around.
If no context is needed, this should be set to NoContext, which is a
context type that can be obtained from any RequestContext via
AsRef.
Sourcetype Future: Future<Item = Self, Error = BoxedError> + Send
type Future: Future<Item = Self, Error = BoxedError> + Send
The future returned by from_request.
Because impl Trait cannot be used inside traits (and named
existential types aren’t yet stable), the type here might not be
nameable. In that case, you can set it to
DefaultFuture<Self, BoxedError> and box the
returned future.
Required Methods§
Sourcefn from_request_and_body(
request: &Arc<Request<()>>,
body: Body,
context: Self::Context,
) -> Self::Future
fn from_request_and_body( request: &Arc<Request<()>>, body: Body, context: Self::Context, ) -> Self::Future
Creates a Self from an HTTP request, asynchronously.
This takes the request metadata, body, and a user-defined context. Only the body is consumed.
Implementations of this function must not block, since this function is
always run on a futures executor. If you need to perform blocking I/O or
long-running computations, you can call tokio_threadpool::blocking.
§Parameters
request: HTTP request data (headers, path, method, etc.).body: The streamed HTTP body.context: The user-defined context.
Provided Methods§
Sourcefn from_request(request: Request<Body>, context: Self::Context) -> Self::Future
fn from_request(request: Request<Body>, context: Self::Context) -> Self::Future
Create a Self from an HTTP request, asynchronously.
This consumes the request and the context.
Implementations of this function must not block, since this function is
always run on a futures executor. If you need to perform blocking I/O or
long-running computations, you can call tokio_threadpool::blocking.
A blocking wrapper around this method is provided by
from_request_sync.
§Parameters
request: An HTTP request from thehttpcrate, containing ahyper::Body.context: User-defined context.
Sourcefn from_request_sync(
request: Request<Body>,
context: Self::Context,
) -> Result<Self, BoxedError>
fn from_request_sync( request: Request<Body>, context: Self::Context, ) -> Result<Self, BoxedError>
Create a Self from an HTTP request, synchronously.
This is a blocking version of from_request. The provided default
implementation will internally create a single-threaded tokio runtime to
perform the conversion and receive the request body.
Note that this does not provide a way to write a blocking version of
from_request. Implementors of this trait must always implement
from_request in a non-blocking fashion, even if they also
implement this method.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.