Trait rocket::data::FromData

source ·
pub trait FromData<'r>: Sized {
    type Error: Send + Debug;

    // Required method
    fn from_data<'life0, 'async_trait>(
        req: &'r Request<'life0>,
        data: Data<'r>
    ) -> Pin<Box<dyn Future<Output = Outcome<'r, Self>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'r: 'async_trait,
             'life0: 'async_trait;
}
Expand description

Trait implemented by data guards to derive a value from request body data.

Data Guards

A data guard is a guard that operates on a request’s body data. Data guards validate and parse request body data via implementations of FromData. In other words, a type is a data guard iff it implements FromData.

Data guards are the target of the data route attribute parameter:

#[post("/submit", data = "<var>")]
fn submit(var: DataGuard) { /* ... */ }

A route can have at most one data guard. Above, 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.

Build-In Guards

Rocket provides implementations for FromData for many types. Their behavior is documented here:

  • Data: Returns the untouched Data.

    • Fails: Never.

    • Succeeds: Always.

    • Forwards: Never.

  • Strings: Cow<str>, &str, &RawStr, String

    Limited by the string data limit.

    Reads the body data into a string via DataStream::into_string().

    • Fails: If the body data is not valid UTF-8 or on I/O errors while reading. The error type is io::Error.

    • Succeeds: If the body data is valid UTF-8. If the limit is exceeded, the string is truncated to the limit.

    • Forwards: Never.

  • Bytes: &[u8], Vec<u8>

    Limited by the bytes data limit.

    Reads the body data into a byte vector via DataStream::into_bytes().

    • Fails: On I/O errors while reading. The error type is io::Error.

    • Succeeds: As long as no I/O error occurs. If the limit is exceeded, the slice is truncated to the limit.

    • Forwards: Never.

  • TempFile

    Limited by the file and/or file/$ext data limit.

    Streams the body data directly into a temporary file. The data is never buffered in memory.

    • Fails: On I/O errors while reading data or creating the temporary file. The error type is io::Error.

    • Succeeds: As long as no I/O error occurs and the temporary file could be created. If the limit is exceeded, only data up to the limit is read and subsequently written.

    • Forwards: Never.

  • Deserializers: Json<T>, MsgPack<T>

    Limited by the json, msgpack data limit, respectively.

    Reads up to the configured limit and deserializes the read data into T using the respective format’s parser.

    • Fails: On I/O errors while reading the data, or if the data fails to parse as a T according to the deserializer. The error type for Json is json::Error and the error type for MsgPack is msgpack::Error.

    • Succeeds: As long as no I/O error occurs and the (limited) body data was successfully deserialized as a T.

    • Forwards: Never.

  • Forms: Form<T>

    Limited by the form or data-form data limit.

    Parses the incoming data stream into fields according to Rocket’s field wire format, pushes each field to T’s FromForm push parser, and finalizes the form. Parsing is done on the stream without reading the data into memory. If the request has as a ContentType::Form, the form limit is applied, otherwise if the request has a ContentType::FormData, the data-form limit is applied.

    • Fails: On I/O errors while reading the data, or if the data fails to parse as a T according to its FromForm implementation. The errors are collected into an Errors, the error type.

    • Succeeds: As long as no I/O error occurs and the (limited) body data was successfully parsed as a T.

    • Forwards: If the request’s Content-Type is neither ContentType::Form nor ContentType::FormData.

  • Option<T>

    Forwards to T’s FromData implementation, capturing the outcome.

    • Fails: Never.

    • Succeeds: Always. If T’s FromData implementation succeeds, the parsed value is returned in Some. If its implementation forwards or fails, None is returned.

    • Forwards: Never.

  • Result<T, T::Error>

    Forwards to T’s FromData implementation, capturing the outcome.

    • Fails: Never.

    • Succeeds: If T’s FromData implementation succeeds or fails. If it succeeds, the value is returned in Ok. If it fails, the error value is returned in Err.

    • Forwards: If T’s implementation forwards.

  • Capped<T>

    Forwards to T’s FromData implementation, recording whether the data was truncated (a.k.a. capped) due to T’s limit being exceeded.

    • Fails: If T’s implementation fails.
    • Succeeds: If T’s implementation succeeds.
    • Forwards: If T’s implementation forwards.

Async Trait

FromData is an async trait. Implementations of FromData must be decorated with an attribute of #[rocket::async_trait]:

use rocket::request::Request;
use rocket::data::{self, Data, FromData};

#[rocket::async_trait]
impl<'r> FromData<'r> for MyType {
    type Error = MyError;

    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
        /* .. */
    }
}

Example

Say that you have a custom type, Person:

struct Person<'r> {
    name: &'r str,
    age: u16
}

Person has a custom serialization format, so the built-in Json type doesn’t suffice. The format is <name>:<age> with Content-Type: application/x-person. You’d like to use Person as a data guard, so that you can retrieve it directly from a client’s request body:

#[post("/person", data = "<person>")]
fn person(person: Person<'_>) -> &'static str {
    "Saved the new person to the database!"
}

A FromData implementation for such a type might look like:

use rocket::request::{self, Request};
use rocket::data::{self, Data, FromData, ToByteUnit};
use rocket::http::{Status, ContentType};
use rocket::outcome::Outcome;

#[derive(Debug)]
enum Error {
    TooLarge,
    NoColon,
    InvalidAge,
    Io(std::io::Error),
}

#[rocket::async_trait]
impl<'r> FromData<'r> for Person<'r> {
    type Error = Error;

    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
        use Error::*;

        // Ensure the content type is correct before opening the data.
        let person_ct = ContentType::new("application", "x-person");
        if req.content_type() != Some(&person_ct) {
            return Outcome::Forward((data, Status::UnsupportedMediaType));
        }

        // Use a configured limit with name 'person' or fallback to default.
        let limit = req.limits().get("person").unwrap_or(256.bytes());

        // Read the data into a string.
        let string = match data.open(limit).into_string().await {
            Ok(string) if string.is_complete() => string.into_inner(),
            Ok(_) => return Outcome::Error((Status::PayloadTooLarge, TooLarge)),
            Err(e) => return Outcome::Error((Status::InternalServerError, Io(e))),
        };

        // We store `string` in request-local cache for long-lived borrows.
        let string = request::local_cache!(req, string);

        // Split the string into two pieces at ':'.
        let (name, age) = match string.find(':') {
            Some(i) => (&string[..i], &string[(i + 1)..]),
            None => return Outcome::Error((Status::UnprocessableEntity, NoColon)),
        };

        // Parse the age.
        let age: u16 = match age.parse() {
            Ok(age) => age,
            Err(_) => return Outcome::Error((Status::UnprocessableEntity, InvalidAge)),
        };

        Outcome::Success(Person { name, age })
    }
}

// The following routes now typecheck...

#[post("/person", data = "<person>")]
fn person(person: Person<'_>) { /* .. */ }

#[post("/person", data = "<person>")]
fn person2(person: Result<Person<'_>, Error>) { /* .. */ }

#[post("/person", data = "<person>")]
fn person3(person: Option<Person<'_>>) { /* .. */ }

#[post("/person", data = "<person>")]
fn person4(person: Person<'_>) -> &str {
    // Note that this is only possible because the data in `person` live
    // as long as the request through request-local cache.
    person.name
}

Required Associated Types§

source

type Error: Send + Debug

The associated error to be returned when the guard fails.

Required Methods§

source

fn from_data<'life0, 'async_trait>( req: &'r Request<'life0>, data: Data<'r> ) -> Pin<Box<dyn Future<Output = Outcome<'r, Self>> + Send + 'async_trait>>where Self: 'async_trait, 'r: 'async_trait, 'life0: 'async_trait,

Asynchronously validates, parses, and converts an instance of Self from the incoming request body data.

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 fails, Error is returned.

Object Safety§

This trait is not object safe.

Implementations on Foreign Types§

source§

impl<'r> FromData<'r> for &'r str

§

type Error = <Capped<&'r str> as FromData<'r>>::Error

source§

fn from_data<'life0, 'async_trait>( r: &'r Request<'life0>, d: Data<'r> ) -> Pin<Box<dyn Future<Output = Outcome<'r, Self>> + Send + 'async_trait>>where Self: 'async_trait, 'r: 'async_trait, 'life0: 'async_trait,

source§

impl<'r> FromData<'r> for &'r [u8]

§

type Error = <Capped<&'r [u8]> as FromData<'r>>::Error

source§

fn from_data<'life0, 'async_trait>( r: &'r Request<'life0>, d: Data<'r> ) -> Pin<Box<dyn Future<Output = Outcome<'r, Self>> + Send + 'async_trait>>where Self: 'async_trait, 'r: 'async_trait, 'life0: 'async_trait,

source§

impl<'r> FromData<'r> for Cow<'_, str>

§

type Error = <Capped<Cow<'_, str>> as FromData<'r>>::Error

source§

fn from_data<'life0, 'async_trait>( r: &'r Request<'life0>, d: Data<'r> ) -> Pin<Box<dyn Future<Output = Outcome<'r, Self>> + Send + 'async_trait>>where Self: 'async_trait, 'r: 'async_trait, 'life0: 'async_trait,

Implementors§

source§

impl<'r> FromData<'r> for &'r RawStr

§

type Error = <Capped<&'r RawStr> as FromData<'r>>::Error

source§

impl<'r> FromData<'r> for TempFile<'_>

§

type Error = <Capped<TempFile<'_>> as FromData<'r>>::Error

source§

impl<'r> FromData<'r> for String

§

type Error = <Capped<String> as FromData<'r>>::Error

source§

impl<'r> FromData<'r> for Vec<u8>

§

type Error = <Capped<Vec<u8>> as FromData<'r>>::Error

source§

impl<'r> FromData<'r> for Capped<&'r str>

§

type Error = Error

source§

impl<'r> FromData<'r> for Capped<&'r RawStr>

§

type Error = Error

source§

impl<'r> FromData<'r> for Capped<&'r [u8]>

§

type Error = Error

source§

impl<'r> FromData<'r> for Capped<TempFile<'_>>

§

type Error = Error

source§

impl<'r> FromData<'r> for Capped<Cow<'_, str>>

§

type Error = Error

source§

impl<'r> FromData<'r> for Capped<String>

§

type Error = Error

source§

impl<'r> FromData<'r> for Capped<Vec<u8>>

§

type Error = Error

source§

impl<'r> FromData<'r> for Data<'r>

source§

impl<'r, T: FromForm<'r>> FromData<'r> for Form<T>

§

type Error = Errors<'r>

source§

impl<'r, T: Deserialize<'r>> FromData<'r> for Json<T>

Available on crate feature json only.
§

type Error = Error<'r>

source§

impl<'r, T: Deserialize<'r>> FromData<'r> for MsgPack<T>

Available on crate feature msgpack only.
§

type Error = Error

source§

impl<'r, T: FromData<'r> + 'r> FromData<'r> for Result<T, T::Error>

source§

impl<'r, T: FromData<'r>> FromData<'r> for Option<T>