Trait rocket::data::FromData [−][src]
pub trait FromData<'a>: Sized {
type Error;
type Owned: Borrow<Self::Borrowed>;
type Borrowed: ?Sized;
fn transform(
request: &Request<'_>,
data: Data
) -> Transform<Outcome<Self::Owned, Self::Error>>;
fn from_data(
request: &Request<'_>,
outcome: Transformed<'a, Self>
) -> Outcome<Self, Self::Error>;
}
Expand description
Trait implemented by data guards to derive a value from request body data.
Data Guards
A data guard is a request guard that operates on a request’s body data.
Data guards validate, parse, and optionally convert request body data.
Validation and parsing/conversion is implemented through FromData
. In
other words, every type that implements FromData
is a data guard.
Data guards are used as the target of the data
route attribute parameter.
A handler can have at most one data guard.
For many data guards, implementing FromDataSimple
will be simpler and
sufficient. All types that implement FromDataSimple
automatically
implement FromData
. Thus, when possible, prefer to implement
FromDataSimple
instead of FromData
.
Example
In the example below, var
is used as the argument name for the data guard
type DataGuard
. When the submit
route matches, Rocket will call the
FromData
implementation for the type T
. The handler will only be called
if the guard returns successfully.
#[post("/submit", data = "<var>")]
fn submit(var: DataGuard) { /* ... */ }
Transforming
Data guards can optionally transform incoming data before processing it
via an implementation of the FromData::transform()
method. This is
useful when a data guard requires or could benefit from a reference to body
data as opposed to an owned version. If a data guard has no need to operate
on a reference to body data, FromDataSimple
should be implemented
instead; it is simpler to implement and less error prone. All types that
implement FromDataSimple
automatically implement FromData
.
When exercising a data guard, Rocket first calls the guard’s
FromData::transform()
method and then subsequently calls the guard’s
FromData::from_data()
method. Rocket stores data returned by
FromData::transform()
on the stack. If transform
returns a
Transform::Owned
, Rocket moves the data back to the data guard in the
subsequent from_data
call as a Transform::Owned
. If instead transform
returns a Transform::Borrowed
variant, Rocket calls borrow()
on the
owned value, producing a borrow of the associated FromData::Borrowed
type and passing it as a Transform::Borrowed
.
Example
Consider a data guard type that wishes to hold a slice to two different parts of the incoming data:
struct Name<'a> {
first: &'a str,
last: &'a str
}
Without the ability to transform into a borrow, implementing such a data
guard would be impossible. With transformation, however, we can instruct
Rocket to produce a borrow to a Data
that has been transformed into a
String
(an &str
).
use std::io::{self, Read};
use rocket::{Request, Data, Outcome::*};
use rocket::data::{FromData, Outcome, Transform, Transformed};
use rocket::http::Status;
const NAME_LIMIT: u64 = 256;
enum NameError {
Io(io::Error),
Parse
}
impl<'a> FromData<'a> for Name<'a> {
type Error = NameError;
type Owned = String;
type Borrowed = str;
fn transform(_: &Request, data: Data) -> Transform<Outcome<Self::Owned, Self::Error>> {
let mut stream = data.open().take(NAME_LIMIT);
let mut string = String::with_capacity((NAME_LIMIT / 2) as usize);
let outcome = match stream.read_to_string(&mut string) {
Ok(_) => Success(string),
Err(e) => Failure((Status::InternalServerError, NameError::Io(e)))
};
// Returning `Borrowed` here means we get `Borrowed` in `from_data`.
Transform::Borrowed(outcome)
}
fn from_data(_: &Request, outcome: Transformed<'a, Self>) -> Outcome<Self, Self::Error> {
// Retrieve a borrow to the now transformed `String` (an &str). This
// is only correct because we know we _always_ return a `Borrowed` from
// `transform` above.
let string = outcome.borrowed()?;
// Perform a crude, inefficient parse.
let splits: Vec<&str> = string.split(" ").collect();
if splits.len() != 2 || splits.iter().any(|s| s.is_empty()) {
return Failure((Status::UnprocessableEntity, NameError::Parse));
}
// Return successfully.
Success(Name { first: splits[0], last: splits[1] })
}
}
Outcomes
The returned Outcome
of a from_data
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 data parameter. As long as all other parsed types succeed, the request will be handled by the requesting handler. -
Failure(Status, E)
If the
Outcome
isFailure
, 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 catchFailure
s and retrieve the error value. -
Forward(Data)
If the
Outcome
isForward
, the request will be forwarded to the next matching request. This requires that no data has been read from theData
parameter. Note that users can request anOption<S>
to catchForward
s.
Provided Implementations
Rocket implements FromData
for several built-in types. Their behavior is
documented here.
-
Data
The identity implementation; simply returns
Data
directly.This implementation always returns successfully.
-
Option<T> where T: FromData
The type
T
is derived from the incoming data usingT
’sFromData
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: FromData
The type
T
is derived from the incoming data usingT
’sFromData
implementation. If derivation is aSuccess
, the value is returned inOk
. If the derivation is aFailure
, the error value is returned inErr
. If the derivation is aForward
, the request is forwarded. -
String
Note: An implementation of
FromData
forString
is only available when compiling in debug mode!Reads the entire request body into a
String
. If reading fails, returns aFailure
with the correspondingio::Error
.WARNING: Do not use this implementation for anything but debugging. This is because the implementation reads the entire body into memory; since the user controls the size of the body, this is an obvious vector for a denial of service attack.
-
Vec<u8>
Note: An implementation of
FromData
forVec<u8>
is only available when compiling in debug mode!Reads the entire request body into a
Vec<u8>
. If reading fails, returns aFailure
with the correspondingio::Error
.WARNING: Do not use this implementation for anything but debugging. This is because the implementation reads the entire body into memory; since the user controls the size of the body, this is an obvious vector for a denial of service attack.
Simplified FromData
For an example of a type that wouldn’t require transformation, see the
FromDataSimple
documentation.
Associated Types
The owned type returned from FromData::transform()
.
The trait bounds ensures that it is is possible to borrow an
&Self::Borrowed
from a value of this type.
The borrowed type consumed by FromData::from_data()
when
FromData::transform()
returns a Transform::Borrowed
.
If FromData::from_data()
returns a Transform::Owned
, this
associated type should be set to Self::Owned
.
Required methods
Transforms data
into a value of type Self::Owned
.
If this method returns a Transform::Owned(Self::Owned)
, then
from_data
should subsequently be called with a data
value of
Transform::Owned(Self::Owned)
. If this method returns a
Transform::Borrowed(Self::Owned)
, from_data
should subsequently be
called with a data
value of Transform::Borrowed(&Self::Borrowed)
. In
other words, the variant of Transform
returned from this method is
used to determine which variant of Transform
should be passed to the
from_data
method. Rocket always makes the subsequent call correctly.
It is very unlikely that a correct implementation of this method is
capable of returning either of an Owned
or Borrowed
variant.
Instead, this method should return exactly one of these variants.
If transformation succeeds, an outcome of Success
is returned.
If the data is not appropriate given the type of Self
, Forward
is
returned. On failure, Failure
is returned.
Validates, parses, and converts the incoming request body data into an
instance of Self
.
If validation and parsing succeeds, an outcome of Success
is returned.
If the data is not appropriate given the type of Self
, Forward
is
returned. If parsing or validation fails, Failure
is returned.
Example
When implementing this method, you rarely need to destruct the outcome
parameter. Instead, the first line of the method should be one of the
following:
// If `Owned` was returned from `transform`:
let data = outcome.owned()?;
// If `Borrowed` was returned from `transform`:
let data = outcome.borrowed()?;
Implementations on Foreign Types
Implementors
The identity implementation of FromData
. Always returns Success
.
Parses a Form
from incoming form data.
If the content type of the request data is not
application/x-www-form-urlencoded
, Forward
s the request. If the form
data cannot be parsed into a T
, a Failure
with status code
UnprocessableEntity
is returned. If the form string is malformed, a
Failure
with status code BadRequest
is returned. Finally, if reading the
incoming stream fails, returns a Failure
with status code
InternalServerError
. In all failure cases, the raw form string is returned
if it was able to be retrieved from the incoming stream.
All relevant warnings and errors are written to the console in Rocket logging format.