Body Data

At some point, your web application will need to process body data. Data processing, like much of Rocket, is type directed. To indicate that a handler expects data, annotate it with data = "<param>", where param is an argument in the handler. The argument’s type must implement the FromData trait. It looks like this, where T: FromData:

  1. #[post("/", data = "<input>")]
  2. fn new(input: T) -> String { ... }

Any type that implements FromData is also known as data guard.

Forms

Forms are the most common type of data handled in web applications, and Rocket makes handling them easy. Say your application is processing a form submission for a new todo Task. The form contains two fields: complete, a checkbox, and description, a text field. You can easily handle the form request in Rocket as follows:

  1. #[derive(FromForm)]
  2. struct Task {
  3. complete: bool,
  4. description: String,
  5. }
  6. #[post("/todo", data = "<task>")]
  7. fn new(task: Form<Task>) -> String { ... }

The Form type implements the FromData trait as long as its generic parameter implements the FromForm trait. In the example, we’ve derived the FromForm trait automatically for the Task structure. FromForm can be derived for any structure whose fields implement FromFormValue. If a POST /todo request arrives, the form data will automatically be parsed into the Task structure. If the data that arrives isn’t of the correct Content-Type, the request is forwarded. If the data doesn’t parse or is simply invalid, a customizable 400 - Bad Request or 422 - Unprocessable Entity error is returned. As before, a forward or failure can be caught by using the Option and Result types:

  1. #[post("/todo", data = "<task>")]
  2. fn new(task: Option<Form<Task>>) -> String { ... }
Lenient Parsing

Rocket’s FromForm parsing is strict by default. In other words, A Form<T> will parse successfully from an incoming form only if the form contains the exact set of fields in T. Said another way, a Form<T> will error on missing and/or extra fields. For instance, if an incoming form contains the fields “a”, “b”, and “c” while T only contains “a” and “c”, the form will not parse as Form<T>.

Rocket allows you to opt-out of this behavior via the LenientForm data type. A LenientForm<T> will parse successfully from an incoming form as long as the form contains a superset of the fields in T. Said another way, a LenientForm<T> automatically discards extra fields without error. For instance, if an incoming form contains the fields “a”, “b”, and “c” while T only contains “a” and “c”, the form will parse as LenientForm<T>.

You can use a LenientForm anywhere you’d use a Form. Its generic parameter is also required to implement FromForm. For instance, we can simply replace Form with LenientForm above to get lenient parsing:

  1. #[derive(FromForm)]
  2. struct Task { .. }
  3. #[post("/todo", data = "<task>")]
  4. fn new(task: LenientForm<Task>) { .. }
Field Renaming

By default, Rocket matches the name of an incoming form field to the name of a structure field. While this behavior is typical, it may also be desired to use different names for form fields and struct fields while still parsing as expected. You can ask Rocket to look for a different form field for a given structure field by using the #[form(field = "name")] field annotation.

As an example, say that you’re writing an application that receives data from an external service. The external service POSTs a form with a field named type. Since type is a reserved keyword in Rust, it cannot be used as the name of a field. To get around this, you can use field renaming as follows:

  1. #[derive(FromForm)]
  2. struct External {
  3. #[form(field = "type")]
  4. api_type: String
  5. }

Rocket will then match the form field named type to the structure field named api_type automatically.

Field Validation

Fields of forms can be easily validated via implementations of the FromFormValue trait. For example, if you’d like to verify that some user is over some age in a form, then you might define a new AdultAge type, use it as a field in a form structure, and implement FromFormValue so that it only validates integers over that age:

  1. struct AdultAge(usize);
  2. impl<'v> FromFormValue<'v> for AdultAge {
  3. type Error = &'v RawStr;
  4. fn from_form_value(form_value: &'v RawStr) -> Result<AdultAge, &'v RawStr> {
  5. match form_value.parse::<usize>() {
  6. Ok(age) if age >= 21 => Ok(AdultAge(age)),
  7. _ => Err(form_value),
  8. }
  9. }
  10. }
  11. #[derive(FromForm)]
  12. struct Person {
  13. age: AdultAge
  14. }

If a form is submitted with a bad age, Rocket won’t call a handler requiring a valid form for that structure. You can use Option or Result types for fields to catch parse failures:

  1. #[derive(FromForm)]
  2. struct Person {
  3. age: Option<AdultAge>
  4. }

The forms validation and forms kitchen sink examples on GitHub provide further illustrations.

JSON

Handling JSON data is no harder: simply use the Json type:

  1. #[derive(Deserialize)]
  2. struct Task {
  3. description: String,
  4. complete: bool
  5. }
  6. #[post("/todo", data = "<task>")]
  7. fn new(task: Json<Task>) -> String { ... }

The only condition is that the generic type in Json implements the Deserialize trait from Serde. See the JSON example on GitHub for a complete example.

Streaming

Sometimes you just want to handle incoming data directly. For example, you might want to stream the incoming data out to a file. Rocket makes this as simple as possible via the Data type:

  1. #[post("/upload", format = "text/plain", data = "<data>")]
  2. fn upload(data: Data) -> io::Result<String> {
  3. data.stream_to_file("/tmp/upload.txt").map(|n| n.to_string())
  4. }

The route above accepts any POST request to the /upload path with Content-Type: text/plain The incoming data is streamed out to tmp/upload.txt, and the number of bytes written is returned as a plain text response if the upload succeeds. If the upload fails, an error response is returned. The handler above is complete. It really is that simple! See the GitHub example code for the full crate.