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 is Success, then the Success 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 is Failure, 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 Failures and retrieve the error value.

  • Forward(Data)

    If the Outcome is Forward, the request will be forwarded to the next matching request. This requires that no data has been read from the Data parameter. Note that users can request an Option<S> to catch Forwards.

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 using T’s FromData 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: FromData

    The type T is derived from the incoming data using T’s FromData implementation. If derivation is a Success, the value is returned in Ok. If the derivation is a Failure, the error value is returned in Err. If the derivation is a Forward, the request is forwarded.

  • String

    Note: An implementation of FromData for String is only available when compiling in debug mode!

    Reads the entire request body into a String. If reading fails, returns a Failure with the corresponding io::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 for Vec<u8> is only available when compiling in debug mode!

    Reads the entire request body into a Vec<u8>. If reading fails, returns a Failure with the corresponding io::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 associated error to be returned when the guard fails.

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