rocket/data/
from_data.rs

1use crate::http::{RawStr, Status};
2use crate::request::{Request, local_cache};
3use crate::data::{Data, Limits};
4use crate::outcome::{self, IntoOutcome, try_outcome, Outcome::*};
5
6/// Type alias for the `Outcome` of [`FromData`].
7///
8/// [`FromData`]: crate::data::FromData
9pub type Outcome<'r, T, E = <T as FromData<'r>>::Error>
10    = outcome::Outcome<T, (Status, E), (Data<'r>, Status)>;
11
12/// Trait implemented by data guards to derive a value from request body data.
13///
14/// # Data Guards
15///
16/// A data guard is a guard that operates on a request's body data. Data guards
17/// validate and parse request body data via implementations of `FromData`. In
18/// other words, a type is a data guard _iff_ it implements `FromData`.
19///
20/// Data guards are the target of the `data` route attribute parameter:
21///
22/// ```rust
23/// # #[macro_use] extern crate rocket;
24/// # type DataGuard = String;
25/// #[post("/submit", data = "<var>")]
26/// fn submit(var: DataGuard) { /* ... */ }
27/// ```
28///
29/// A route can have at most one data guard. Above, `var` is used as the
30/// argument name for the data guard type `DataGuard`. When the `submit` route
31/// matches, Rocket will call the `FromData` implementation for the type `T`.
32/// The handler will only be called if the guard returns successfully.
33///
34/// ## Build-In Guards
35///
36/// Rocket provides implementations for `FromData` for many types. Their
37/// behavior is documented here:
38///
39///   * `Data`: Returns the untouched `Data`.
40///
41///     - **Fails:** Never.
42///
43///     - **Succeeds:** Always.
44///
45///     - **Forwards:** Never.
46///
47///   * Strings: `Cow<str>`, `&str`, `&RawStr`, `String`
48///
49///     _Limited by the `string` [data limit]._
50///
51///     Reads the body data into a string via [`DataStream::into_string()`].
52///
53///     - **Fails:** If the body data is not valid UTF-8 or on I/O errors while
54///     reading. The error type is [`io::Error`].
55///
56///     - **Succeeds:** If the body data _is_ valid UTF-8. If the limit is
57///     exceeded, the string is truncated to the limit.
58///
59///     - **Forwards:** Never.
60///
61///   * Bytes: `&[u8]`, `Vec<u8>`
62///
63///     _Limited by the `bytes` [data limit]._
64///
65///     Reads the body data into a byte vector via [`DataStream::into_bytes()`].
66///
67///     - **Fails:** On I/O errors while reading. The error type is
68///     [`io::Error`].
69///
70///     - **Succeeds:** As long as no I/O error occurs. If the limit is
71///     exceeded, the slice is truncated to the limit.
72///
73///     - **Forwards:** Never.
74///
75///   * [`TempFile`](crate::fs::TempFile)
76///
77///     _Limited by the `file` and/or `file/$ext` [data limit]._
78///
79///     Streams the body data directly into a temporary file. The data is never
80///     buffered in memory.
81///
82///     - **Fails:** On I/O errors while reading data or creating the temporary
83///     file. The error type is [`io::Error`].
84///
85///     - **Succeeds:** As long as no I/O error occurs and the temporary file
86///     could be created. If the limit is exceeded, only data up to the limit is
87///     read and subsequently written.
88///
89///     - **Forwards:** Never.
90///
91///   * Deserializers: [`Json<T>`], [`MsgPack<T>`]
92///
93///     _Limited by the `json`, `msgpack` [data limit], respectively._
94///
95///     Reads up to the configured limit and deserializes the read data into `T`
96///     using the respective format's parser.
97///
98///     - **Fails:** On I/O errors while reading the data, or if the data fails
99///     to parse as a `T` according to the deserializer. The error type for
100///     `Json` is [`json::Error`](crate::serde::json::Error) and the error type
101///     for `MsgPack` is [`msgpack::Error`](crate::serde::msgpack::Error).
102///
103///     - **Succeeds:** As long as no I/O error occurs and the (limited) body
104///     data was successfully deserialized as a `T`.
105///
106///     - **Forwards:** Never.
107///
108///   * Forms: [`Form<T>`]
109///
110///     _Limited by the `form` or `data-form` [data limit]._
111///
112///     Parses the incoming data stream into fields according to Rocket's [field
113///     wire format], pushes each field to `T`'s [`FromForm`] [push parser], and
114///     finalizes the form. Parsing is done on the stream without reading the
115///     data into memory. If the request has as a [`ContentType::Form`], the
116///     `form` limit is applied, otherwise if the request has a
117///     [`ContentType::FormData`], the `data-form` limit is applied.
118///
119///     - **Fails:** On I/O errors while reading the data, or if the data fails
120///     to parse as a `T` according to its `FromForm` implementation. The errors
121///     are collected into an [`Errors`](crate::form::Errors), the error type.
122///
123///     - **Succeeds:** As long as no I/O error occurs and the (limited) body
124///     data was successfully parsed as a `T`.
125///
126///     - **Forwards:** If the request's `Content-Type` is neither
127///     [`ContentType::Form`] nor [`ContentType::FormData`].
128///
129///   * `Option<T>`
130///
131///     Forwards to `T`'s `FromData` implementation, capturing the outcome.
132///
133///     - **Fails:** Never.
134///
135///     - **Succeeds:** Always. If `T`'s `FromData` implementation succeeds, the
136///     parsed value is returned in `Some`. If its implementation forwards or
137///     fails, `None` is returned.
138///
139///     - **Forwards:** Never.
140///
141///   * `Result<T, T::Error>`
142///
143///     Forwards to `T`'s `FromData` implementation, capturing the outcome.
144///
145///     - **Fails:** Never.
146///
147///     - **Succeeds:** If `T`'s `FromData` implementation succeeds or fails. If
148///     it succeeds, the value is returned in `Ok`. If it fails, the error value
149///     is returned in `Err`.
150///
151///     - **Forwards:** If `T`'s implementation forwards.
152///
153///   * [`Capped<T>`]
154///
155///     Forwards to `T`'s `FromData` implementation, recording whether the data
156///     was truncated (a.k.a. capped) due to `T`'s limit being exceeded.
157///
158///     - **Fails:** If `T`'s implementation fails.
159///     - **Succeeds:** If `T`'s implementation succeeds.
160///     - **Forwards:** If `T`'s implementation forwards.
161///
162/// [data limit]: crate::data::Limits#built-in-limits
163/// [`DataStream::into_string()`]: crate::data::DataStream::into_string()
164/// [`DataStream::into_bytes()`]: crate::data::DataStream::into_bytes()
165/// [`io::Error`]: std::io::Error
166/// [`Json<T>`]: crate::serde::json::Json
167/// [`MsgPack<T>`]: crate::serde::msgpack::MsgPack
168/// [`Form<T>`]: crate::form::Form
169/// [field wire format]: crate::form#field-wire-format
170/// [`FromForm`]: crate::form::FromForm
171/// [push parser]: crate::form::FromForm#push-parsing
172/// [`ContentType::Form`]: crate::http::ContentType::Form
173/// [`ContentType::FormData`]: crate::http::ContentType::FormData
174///
175/// ## Async Trait
176///
177/// [`FromData`] is an _async_ trait. Implementations of `FromData` must be
178/// decorated with an attribute of `#[rocket::async_trait]`:
179///
180/// ```rust
181/// use rocket::request::Request;
182/// use rocket::data::{self, Data, FromData};
183/// # struct MyType;
184/// # type MyError = String;
185///
186/// #[rocket::async_trait]
187/// impl<'r> FromData<'r> for MyType {
188///     type Error = MyError;
189///
190///     async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
191///         /* .. */
192///         # unimplemented!()
193///     }
194/// }
195/// ```
196///
197/// # Example
198///
199/// Say that you have a custom type, `Person`:
200///
201/// ```rust
202/// struct Person<'r> {
203///     name: &'r str,
204///     age: u16
205/// }
206/// ```
207///
208/// `Person` has a custom serialization format, so the built-in `Json` type
209/// doesn't suffice. The format is `<name>:<age>` with `Content-Type:
210/// application/x-person`. You'd like to use `Person` as a data guard, so that
211/// you can retrieve it directly from a client's request body:
212///
213/// ```rust
214/// # use rocket::post;
215/// # type Person<'r> = &'r rocket::http::RawStr;
216/// #[post("/person", data = "<person>")]
217/// fn person(person: Person<'_>) -> &'static str {
218///     "Saved the new person to the database!"
219/// }
220/// ```
221///
222/// A `FromData` implementation for such a type might look like:
223///
224/// ```rust
225/// # #[macro_use] extern crate rocket;
226/// #
227/// # #[derive(Debug)]
228/// # struct Person<'r> { name: &'r str, age: u16 }
229/// #
230/// use rocket::request::{self, Request};
231/// use rocket::data::{self, Data, FromData, ToByteUnit};
232/// use rocket::http::{Status, ContentType};
233/// use rocket::outcome::Outcome;
234///
235/// #[derive(Debug)]
236/// enum Error {
237///     TooLarge,
238///     NoColon,
239///     InvalidAge,
240///     Io(std::io::Error),
241/// }
242///
243/// #[rocket::async_trait]
244/// impl<'r> FromData<'r> for Person<'r> {
245///     type Error = Error;
246///
247///     async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
248///         use Error::*;
249///
250///         // Ensure the content type is correct before opening the data.
251///         let person_ct = ContentType::new("application", "x-person");
252///         if req.content_type() != Some(&person_ct) {
253///             return Outcome::Forward((data, Status::UnsupportedMediaType));
254///         }
255///
256///         // Use a configured limit with name 'person' or fallback to default.
257///         let limit = req.limits().get("person").unwrap_or(256.bytes());
258///
259///         // Read the data into a string.
260///         let string = match data.open(limit).into_string().await {
261///             Ok(string) if string.is_complete() => string.into_inner(),
262///             Ok(_) => return Outcome::Error((Status::PayloadTooLarge, TooLarge)),
263///             Err(e) => return Outcome::Error((Status::InternalServerError, Io(e))),
264///         };
265///
266///         // We store `string` in request-local cache for long-lived borrows.
267///         let string = request::local_cache!(req, string);
268///
269///         // Split the string into two pieces at ':'.
270///         let (name, age) = match string.find(':') {
271///             Some(i) => (&string[..i], &string[(i + 1)..]),
272///             None => return Outcome::Error((Status::UnprocessableEntity, NoColon)),
273///         };
274///
275///         // Parse the age.
276///         let age: u16 = match age.parse() {
277///             Ok(age) => age,
278///             Err(_) => return Outcome::Error((Status::UnprocessableEntity, InvalidAge)),
279///         };
280///
281///         Outcome::Success(Person { name, age })
282///     }
283/// }
284///
285/// // The following routes now typecheck...
286///
287/// #[post("/person", data = "<person>")]
288/// fn person(person: Person<'_>) { /* .. */ }
289///
290/// #[post("/person", data = "<person>")]
291/// fn person2(person: Result<Person<'_>, Error>) { /* .. */ }
292///
293/// #[post("/person", data = "<person>")]
294/// fn person3(person: Option<Person<'_>>) { /* .. */ }
295///
296/// #[post("/person", data = "<person>")]
297/// fn person4(person: Person<'_>) -> &str {
298///     // Note that this is only possible because the data in `person` live
299///     // as long as the request through request-local cache.
300///     person.name
301/// }
302/// ```
303#[crate::async_trait]
304pub trait FromData<'r>: Sized {
305    /// The associated error to be returned when the guard fails.
306    type Error: Send + std::fmt::Debug;
307
308    /// Asynchronously validates, parses, and converts an instance of `Self`
309    /// from the incoming request body data.
310    ///
311    /// If validation and parsing succeeds, an outcome of `Success` is returned.
312    /// If the data is not appropriate given the type of `Self`, `Forward` is
313    /// returned. If parsing fails, `Error` is returned.
314    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self>;
315}
316
317use crate::data::Capped;
318
319#[crate::async_trait]
320impl<'r> FromData<'r> for Capped<String> {
321    type Error = std::io::Error;
322
323    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
324        let limit = req.limits().get("string").unwrap_or(Limits::STRING);
325        data.open(limit).into_string().await.or_error(Status::BadRequest)
326    }
327}
328
329impl_strict_from_data_from_capped!(String);
330
331#[crate::async_trait]
332impl<'r> FromData<'r> for Capped<&'r str> {
333    type Error = std::io::Error;
334
335    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
336        let capped = try_outcome!(<Capped<String>>::from_data(req, data).await);
337        let string = capped.map(|s| local_cache!(req, s));
338        Success(string)
339    }
340}
341
342impl_strict_from_data_from_capped!(&'r str);
343
344#[crate::async_trait]
345impl<'r> FromData<'r> for Capped<&'r RawStr> {
346    type Error = std::io::Error;
347
348    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
349        let capped = try_outcome!(<Capped<String>>::from_data(req, data).await);
350        let raw = capped.map(|s| RawStr::new(local_cache!(req, s)));
351        Success(raw)
352    }
353}
354
355impl_strict_from_data_from_capped!(&'r RawStr);
356
357#[crate::async_trait]
358impl<'r> FromData<'r> for Capped<std::borrow::Cow<'_, str>> {
359    type Error = std::io::Error;
360
361    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
362        let capped = try_outcome!(<Capped<String>>::from_data(req, data).await);
363        Success(capped.map(|s| s.into()))
364    }
365}
366
367impl_strict_from_data_from_capped!(std::borrow::Cow<'_, str>);
368
369#[crate::async_trait]
370impl<'r> FromData<'r> for Capped<&'r [u8]> {
371    type Error = std::io::Error;
372
373    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
374        let capped = try_outcome!(<Capped<Vec<u8>>>::from_data(req, data).await);
375        let raw = capped.map(|b| local_cache!(req, b));
376        Success(raw)
377    }
378}
379
380impl_strict_from_data_from_capped!(&'r [u8]);
381
382#[crate::async_trait]
383impl<'r> FromData<'r> for Capped<Vec<u8>> {
384    type Error = std::io::Error;
385
386    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
387        let limit = req.limits().get("bytes").unwrap_or(Limits::BYTES);
388        data.open(limit).into_bytes().await.or_error(Status::BadRequest)
389    }
390}
391
392impl_strict_from_data_from_capped!(Vec<u8>);
393
394#[crate::async_trait]
395impl<'r> FromData<'r> for Data<'r> {
396    type Error = std::convert::Infallible;
397
398    async fn from_data(_: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
399        Success(data)
400    }
401}
402
403#[crate::async_trait]
404impl<'r, T: FromData<'r> + 'r> FromData<'r> for Result<T, T::Error> {
405    type Error = std::convert::Infallible;
406
407    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
408        match T::from_data(req, data).await {
409            Success(v) => Success(Ok(v)),
410            Error((_, e)) => Success(Err(e)),
411            Forward(d) => Forward(d),
412        }
413    }
414}
415
416#[crate::async_trait]
417impl<'r, T: FromData<'r>> FromData<'r> for Option<T> {
418    type Error = std::convert::Infallible;
419
420    async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
421        match T::from_data(req, data).await {
422            Success(v) => Success(Some(v)),
423            Error(..) | Forward(..) => Success(None),
424        }
425    }
426}