Rocket/site/guide/4-requests.md

1812 lines
58 KiB
Markdown

# Requests
Together, a [`route`] attribute and function signature specify what must be true
about a request in order for the route's handler to be called. You've already
seen an example of this in action:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
#[get("/world")]
fn handler() { /* .. */ }
```
This route indicates that it only matches against `GET` requests to the `/world`
route. Rocket ensures that this is the case before `handler` is called. Of
course, you can do much more than specify the method and path of a request.
Among other things, you can ask Rocket to automatically validate:
* The type of a dynamic path segment.
* The type of _several_ dynamic path segments.
* The type of incoming body data.
* The types of query strings, forms, and form values.
* The expected incoming or outgoing format of a request.
* Any arbitrary, user-defined security or validation policies.
The route attribute and function signature work in tandem to describe these
validations. Rocket's code generation takes care of actually validating the
properties. This section describes how to ask Rocket to validate against all of
these properties and more.
[`route`]: @api/rocket/attr.route.html
## Methods
A Rocket route attribute can be any one of `get`, `put`, `post`, `delete`,
`head`, `patch`, or `options`, each corresponding to the HTTP method to match
against. For example, the following attribute will match against `POST` requests
to the root path:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
#[post("/")]
# fn handler() {}
```
The grammar for these attributes is defined formally in the [`route`] API docs.
### HEAD Requests
Rocket handles `HEAD` requests automatically when there exists a `GET` route
that would otherwise match. It does this by stripping the body from the
response, if there is one. You can also specialize the handling of a `HEAD`
request by declaring a route for it; Rocket won't interfere with `HEAD` requests
your application explicitly handles.
### Reinterpreting
Because HTML forms can only be directly submitted as `GET` or `POST` requests,
Rocket _reinterprets_ request methods under certain conditions. If a `POST`
request contains a body of `Content-Type: application/x-www-form-urlencoded` and
the form's **first** field has the name `_method` and a valid HTTP method name
as its value (such as `"PUT"`), that field's value is used as the method for the
incoming request. This allows Rocket applications to submit non-`POST` forms.
The [todo example](@example/todo/static/index.html.tera#L47) makes use of this
feature to submit `PUT` and `DELETE` requests from a web form.
## Dynamic Paths
You can declare path segments as dynamic by using angle brackets around variable
names in a route's path. For example, if we want to say _Hello!_ to anything,
not just the world, we can declare a route like so:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
#[get("/hello/<name>")]
fn hello(name: &str) -> String {
format!("Hello, {}!", name)
}
```
If we were to mount the path at the root (`.mount("/", routes![hello])`), then
any request to a path with two non-empty segments, where the first segment is
`hello`, will be dispatched to the `hello` route. For example, if we were to
visit `/hello/John`, the application would respond with `Hello, John!`.
Any number of dynamic path segments are allowed. A path segment can be of any
type, including your own, as long as the type implements the [`FromParam`]
trait. We call these types _parameter guards_. Rocket implements `FromParam` for
many of the standard library types, as well as a few special Rocket types. For
the full list of provided implementations, see the [`FromParam` API docs].
Here's a more complete route to illustrate varied usage:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
#[get("/hello/<name>/<age>/<cool>")]
fn hello(name: String, age: u8, cool: bool) -> String {
if cool {
format!("You're a cool {} year old, {}!", age, name)
} else {
format!("{}, we need to talk about your coolness.", name)
}
}
```
[`FromParam`]: @api/rocket/request/trait.FromParam.html
[`FromParam` API docs]: @api/rocket/request/trait.FromParam.html
### Multiple Segments
You can also match against multiple segments by using `<param..>` in a route
path. The type of such parameters, known as _segments guards_, must implement
[`FromSegments`]. A segments guard must be the final component of a path: any
text after a segments guard will result in a compile-time error.
As an example, the following route matches against all paths that begin with
`/page`:
```rust
# use rocket::get;
use std::path::PathBuf;
#[get("/page/<path..>")]
fn get_page(path: PathBuf) { /* ... */ }
```
The path after `/page/` will be available in the `path` parameter, which may be
empty for paths that are simply `/page`, `/page/`, `/page//`, and so on. The
`FromSegments` implementation for `PathBuf` ensures that `path` cannot lead to
[path traversal attacks]. With this, a safe and secure static file server can be
implemented in just 4 lines:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
use std::path::{Path, PathBuf};
use rocket::response::NamedFile;
#[get("/<file..>")]
async fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("static/").join(file)).await.ok()
}
```
[path traversal attacks]: https://www.owasp.org/index.php/Path_Traversal
! tip: Rocket makes it even _easier_ to serve static files!
If you need to serve static files from your Rocket application, consider using
the [`StaticFiles`] custom handler from [`rocket_contrib`], which makes it as
simple as:
`rocket.mount("/public", StaticFiles::from("static/"))`
[`rocket_contrib`]: @api/rocket_contrib/
[`StaticFiles`]: @api/rocket_contrib/serve/struct.StaticFiles.html
[`FromSegments`]: @api/rocket/request/trait.FromSegments.html
## Forwarding
Let's take a closer look at the route attribute and signature pair from a
previous example:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
#[get("/hello/<name>/<age>/<cool>")]
fn hello(name: String, age: u8, cool: bool) { /* ... */ }
```
What if `cool` isn't a `bool`? Or, what if `age` isn't a `u8`? When a parameter
type mismatch occurs, Rocket _forwards_ the request to the next matching route,
if there is any. This continues until a route doesn't forward the request or
there are no remaining routes to try. When there are no remaining routes, a
customizable **404 error** is returned.
Routes are attempted in increasing _rank_ order. Rocket chooses a default
ranking from -6 to -1, detailed in the next section, but a route's rank can also
be manually set with the `rank` attribute. To illustrate, consider the following
routes:
```rust
# #[macro_use] extern crate rocket;
#[get("/user/<id>")]
fn user(id: usize) { /* ... */ }
#[get("/user/<id>", rank = 2)]
fn user_int(id: isize) { /* ... */ }
#[get("/user/<id>", rank = 3)]
fn user_str(id: &str) { /* ... */ }
#[launch]
fn rocket() -> rocket::Rocket {
rocket::ignite().mount("/", routes![user, user_int, user_str])
}
```
Notice the `rank` parameters in `user_int` and `user_str`. If we run this
application with the routes mounted at the root path, as is done in `rocket()`
above, requests to `/user/<id>` (such as `/user/123`, `/user/Bob`, and so on)
will be routed as follows:
1. The `user` route matches first. If the string at the `<id>` position is an
unsigned integer, then the `user` handler is called. If it is not, then the
request is forwarded to the next matching route: `user_int`.
2. The `user_int` route matches next. If `<id>` is a signed integer,
`user_int` is called. Otherwise, the request is forwarded.
3. The `user_str` route matches last. Since `<id>` is always a string, the
route always matches. The `user_str` handler is called.
! note: A route's rank appears in **[brackets]** during launch.
You'll also find a route's rank logged in brackets during application launch:
`GET /user/<id> [3] (user_str)`.
Forwards can be _caught_ by using a `Result` or `Option` type. For example, if
the type of `id` in the `user` function was `Result<usize, &str>`, then `user`
would never forward. An `Ok` variant would indicate that `<id>` was a valid
`usize`, while an `Err` would indicate that `<id>` was not a `usize`. The
`Err`'s value would contain the string that failed to parse as a `usize`.
! tip: It's not just forwards that can be caught!
In general, when any guard fails for any reason, including parameter guards,
you can use an `Option` or `Result` type in its place to catch the failure.
By the way, if you were to omit the `rank` parameter in the `user_str` or
`user_int` routes, Rocket would emit an error and abort launch, indicating that
the routes _collide_, or can match against similar incoming requests. The `rank`
parameter resolves this collision.
### Default Ranking
If a rank is not explicitly specified, Rocket assigns a default rank. The
default rank prefers static segments over dynamic segments in both paths and
queries: the _more_ static a route's path and query are, the higher its
precedence.
There are three "colors" to paths and queries:
1. `static`, meaning all components are static
2. `partial`, meaning at least one component is dynamic
3. `wild`, meaning all components are dynamic
Static paths carry more weight than static queries. The same is true for partial
and wild paths. This results in the following default ranking table:
| path color | query color | default rank |
|------------|-------------|--------------|
| static | static | -12 |
| static | partial | -11 |
| static | wild | -10 |
| static | none | -9 |
| partial | static | -8 |
| partial | partial | -7 |
| partial | wild | -6 |
| partial | none | -5 |
| wild | static | -4 |
| wild | partial | -3 |
| wild | wild | -2 |
| wild | none | -1 |
Recall that _lower_ ranks have _higher_ precedence.
## Request Guards
Request guards are one of Rocket's most powerful instruments. As the name might
imply, a request guard protects a handler from being called erroneously based on
information contained in an incoming request. More specifically, a request guard
is a type that represents an arbitrary validation policy. The validation policy
is implemented through the [`FromRequest`] trait. Every type that implements
`FromRequest` is a request guard.
Request guards appear as inputs to handlers. An arbitrary number of request
guards can appear as arguments in a route handler. Rocket will automatically
invoke the [`FromRequest`] implementation for request guards before calling the
handler. Rocket only dispatches requests to a handler when all of its guards
pass.
For instance, the following dummy handler makes use of three request guards,
`A`, `B`, and `C`. An input can be identified as a request guard if it is not
named in the route attribute.
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
# type A = rocket::http::Method;
# type B = A;
# type C = A;
#[get("/<param>")]
fn index(param: isize, a: A, b: B, c: C) { /* ... */ }
```
Request guards always fire in left-to-right declaration order. In the example
above, the order will be `A` followed by `B` followed by `C`. Failure is
short-circuiting; if one guard fails, the remaining are not attempted. To learn
more about request guards and implementing them, see the [`FromRequest`]
documentation.
[`FromRequest`]: @api/rocket/request/trait.FromRequest.html
[`CookieJar`]: @api/rocket/http/struct.CookieJar.html
### Custom Guards
You can implement `FromRequest` for your own types. For instance, to protect a
`sensitive` route from running unless an `ApiKey` is present in the request
headers, you might create an `ApiKey` type that implements `FromRequest` and
then use it as a request guard:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
# type ApiKey = rocket::http::Method;
#[get("/sensitive")]
fn sensitive(key: ApiKey) { /* .. */ }
```
You might also implement `FromRequest` for an `AdminUser` type that
authenticates an administrator using incoming cookies. Then, any handler with an
`AdminUser` or `ApiKey` type in its argument list is assured to only be invoked
if the appropriate conditions are met. Request guards centralize policies,
resulting in a simpler, safer, and more secure applications.
### Guard Transparency
When a request guard type can only be created through its [`FromRequest`]
implementation, and the type is not `Copy`, the existence of a request guard
value provides a _type-level proof_ that the current request has been validated
against an arbitrary policy. This provides powerful means of protecting your
application against access-control violations by requiring data accessing
methods to _witness_ a proof of authorization via a request guard. We call the
notion of using a request guard as a witness _guard transparency_.
As a concrete example, the following application has a function,
`health_records`, that returns all of the health records in a database. Because
health records are sensitive information, they should only be accessible by
super users. The `SuperUser` request guard authenticates and authorizes a super
user, and its `FromRequest` implementation is the only means by which a
`SuperUser` can be constructed. By declaring the `health_records` function as
follows, access control violations against health records are guaranteed to be
prevented at _compile-time_:
```rust
# type Records = ();
# type SuperUser = ();
fn health_records(user: &SuperUser) -> Records { /* ... */ }
```
The reasoning is as follows:
1. The `health_records` function requires an `&SuperUser` type.
2. The only constructor for a `SuperUser` type is `FromRequest`.
3. Only Rocket can provide an active `&Request` to construct via `FromRequest`.
4. Thus, there must be a `Request` authorizing a `SuperUser` to call
`health_records`.
! note
At the expense of a lifetime parameter in the guard type, guarantees can be
made even stronger by tying the lifetime of the `Request` passed to
`FromRequest` to the request guard, ensuring that the guard value always
corresponds to an _active_ request.
We recommend leveraging request guard transparency for _all_ data accesses.
### Forwarding Guards
Request guards and forwarding are a powerful combination for enforcing policies.
To illustrate, we consider how a simple authorization system might be
implemented using these mechanisms.
We start with two request guards:
* `User`: A regular, authenticated user.
The `FromRequest` implementation for `User` checks that a cookie identifies
a user and returns a `User` value if so. If no user can be authenticated,
the guard forwards.
* `AdminUser`: A user authenticated as an administrator.
The `FromRequest` implementation for `AdminUser` checks that a cookie
identifies an _administrative_ user and returns an `AdminUser` value if so.
If no user can be authenticated, the guard forwards.
We now use these two guards in combination with forwarding to implement the
following three routes, each leading to an administrative control panel at
`/admin`:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
# type Template = ();
# type AdminUser = rocket::http::Method;
# type User = rocket::http::Method;
use rocket::response::Redirect;
#[get("/login")]
fn login() -> Template { /* .. */ }
#[get("/admin")]
fn admin_panel(admin: AdminUser) -> &'static str {
"Hello, administrator. This is the admin panel!"
}
#[get("/admin", rank = 2)]
fn admin_panel_user(user: User) -> &'static str {
"Sorry, you must be an administrator to access this page."
}
#[get("/admin", rank = 3)]
fn admin_panel_redirect() -> Redirect {
Redirect::to(uri!(login))
}
```
The three routes above encode authentication _and_ authorization. The
`admin_panel` route only succeeds if an administrator is logged in. Only then is
the admin panel displayed. If the user is not an admin, the `AdminUser` guard
will forward. Since the `admin_panel_user` route is ranked next highest, it is
attempted next. This route succeeds if there is _any_ user signed in, and an
authorization failure message is displayed. Finally, if a user isn't signed in,
the `admin_panel_redirect` route is attempted. Since this route has no guards,
it always succeeds. The user is redirected to a log in page.
## Cookies
A reference to a [`CookieJar`] is an important, built-in request guard: it
allows you to get, set, and remove cookies. Because `&CookieJar` is a request
guard, an argument of its type can simply be added to a handler:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
use rocket::http::CookieJar;
#[get("/")]
fn index(cookies: &CookieJar<'_>) -> Option<String> {
cookies.get("message").map(|crumb| format!("Message: {}", crumb.value()))
}
```
This results in the incoming request's cookies being accessible from the
handler. The example above retrieves a cookie named `message`. Cookies can also
be set and removed using the `CookieJar` guard. The [cookies example] on GitHub
illustrates further use of the `CookieJar` type to get and set cookies, while
the [`CookieJar`] documentation contains complete usage information.
[cookies example]: @example/cookies
### Private Cookies
Cookies added via the [`CookieJar::add()`] method are set _in the clear._ In
other words, the value set is visible to the client. For sensitive data, Rocket
provides _private_ cookies. Private cookies are similar to regular cookies
except that they are encrypted using authenticated encryption, a form of
encryption which simultaneously provides confidentiality, integrity, and
authenticity. Thus, private cookies cannot be inspected, tampered with, or
manufactured by clients. If you prefer, you can think of private cookies as
being signed and encrypted.
Support for private cookies must be manually enabled via the `secrets` crate
feature:
```toml
## in Cargo.toml
rocket = { version = "0.5.0-dev", features = ["secrets"] }
```
The API for retrieving, adding, and removing private cookies is identical except
methods are suffixed with `_private`. These methods are: [`get_private`],
[`get_private_pending`], [`add_private`], and [`remove_private`]. An example of
their usage is below:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
use rocket::http::{Cookie, CookieJar};
use rocket::response::{Flash, Redirect};
/// Retrieve the user's ID, if any.
#[get("/user_id")]
fn user_id(cookies: &CookieJar<'_>) -> Option<String> {
cookies.get_private("user_id")
.map(|crumb| format!("User ID: {}", crumb.value()))
}
/// Remove the `user_id` cookie.
#[post("/logout")]
fn logout(cookies: &CookieJar<'_>) -> Flash<Redirect> {
cookies.remove_private(Cookie::named("user_id"));
Flash::success(Redirect::to("/"), "Successfully logged out.")
}
```
[`CookieJar::add()`]: @api/rocket/http/struct.CookieJar.html#method.add
### Secret Key
To encrypt private cookies, Rocket uses the 256-bit key specified in the
`secret_key` configuration parameter. When compiled in debug mode, a fresh key
is generated automatically. In release mode, Rocket requires you to set a secret
key if the `secrets` feature is enabled. Failure to do so results in a hard
error at launch time. The value of the parameter may either be a 256-bit base64
or hex string or a 32-byte slice.
Generating a string suitable for use as a `secret_key` configuration value is
usually done through tools like `openssl`. Using `openssl`, a 256-bit base64 key
can be generated with the command `openssl rand -base64 32`.
For more information on configuration, see the [Configuration](../configuration)
section of the guide.
[`get_private`]: @api/rocket/http/struct.CookieJar.html#method.get_private
[`get_private_pending`]: @api/rocket/http/struct.CookieJar.html#method.get_private_pending
[`add_private`]: @api/rocket/http/struct.CookieJar.html#method.add_private
[`remove_private`]: @api/rocket/http/struct.CookieJar.html#method.remove_private
## Format
A route can specify the data format it is willing to accept or respond with by
using the `format` route parameter. The value of the parameter is a string
identifying an HTTP media type or a shorthand variant. For instance, for JSON
data, the string `application/json` or simply `json` can be used.
When a route indicates a payload-supporting method (`PUT`, `POST`, `DELETE`, and
`PATCH`), the `format` route parameter instructs Rocket to check against the
`Content-Type` header of the incoming request. Only requests where the
`Content-Type` header matches the `format` parameter will match to the route.
As an example, consider the following route:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
# type User = rocket::data::Data;
#[post("/user", format = "application/json", data = "<user>")]
fn new_user(user: User) { /* ... */ }
```
The `format` parameter in the `post` attribute declares that only incoming
requests with `Content-Type: application/json` will match `new_user`. (The
`data` parameter is described in the next section.) Shorthand is also supported
for the most common `format` arguments. Instead of using the full Content-Type,
`format = "application/json"`, you can also write shorthands like `format =
"json"`. For a full list of available shorthands, see the
[`ContentType::parse_flexible()`] documentation.
When a route indicates a non-payload-supporting method (`GET`, `HEAD`,
`OPTIONS`) the `format` route parameter instructs Rocket to check against the
`Accept` header of the incoming request. Only requests where the preferred media
type in the `Accept` header matches the `format` parameter will match to the
route.
As an example, consider the following route:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
# type User = ();
#[get("/user/<id>", format = "json")]
fn user(id: usize) -> User { /* .. */ }
```
The `format` parameter in the `get` attribute declares that only incoming
requests with `application/json` as the preferred media type in the `Accept`
header will match `user`. If instead the route had been declared as `post`,
Rocket would match the `format` against the `Content-Type` header of the
incoming response.
[`ContentType::parse_flexible()`]: @api/rocket/http/struct.ContentType.html#method.parse_flexible
## Body Data
Body data processing, like much of Rocket, is type directed. To indicate that a
handler expects body 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` is assumed to implement `FromData`:
```rust
# #[macro_use] extern crate rocket;
# type T = rocket::data::Data;
#[post("/", data = "<input>")]
fn new(input: T) { /* .. */ }
```
Any type that implements [`FromData`] is also known as _a data guard_.
[`FromData`]: @api/rocket/data/trait.FromData.html
### JSON
The [`Json<T>`](@api/rocket_contrib/json/struct.Json.html) type from
[`rocket_contrib`] is a data guard that parses the deserialzies body data as
JSON. The only condition is that the generic type `T` implements the
`Deserialize` trait from [Serde](https://github.com/serde-rs/json).
```rust
# #[macro_use] extern crate rocket;
# extern crate rocket_contrib;
# fn main() {}
use serde::Deserialize;
use rocket_contrib::json::Json;
#[derive(Deserialize)]
struct Task {
description: String,
complete: bool
}
#[post("/todo", data = "<task>")]
fn new(task: Json<Task>) { /* .. */ }
```
See the [JSON example] on GitHub for a complete example.
[JSON example]: @example/json
### Temporary Files
The [`TempFile`] data guard streams data directly to a temporary file which can
the be persisted. It makes accepting file uploads trivial:
```rust
# #[macro_use] extern crate rocket;
use rocket::data::TempFile;
#[post("/upload", format = "plain", data = "<file>")]
async fn upload(mut file: TempFile<'_>) -> std::io::Result<()> {
# let permanent_location = "/tmp/perm.txt";
file.persist_to(permanent_location).await
}
```
[`TempFile`]: @api/rocket/data/struct.TempFile.html
### Streaming
Sometimes you just want to handle incoming data directly. For example, you might
want to stream the incoming data to some sink. Rocket makes this as simple as
possible via the [`Data`](@api/rocket/data/struct.Data.html) type:
```rust
# #[macro_use] extern crate rocket;
use rocket::tokio;
use rocket::data::{Data, ToByteUnit};
#[post("/debug", data = "<data>")]
async fn debug(data: Data) -> std::io::Result<()> {
// Stream at most 512KiB all of the body data to stdout.
data.open(512.kibibytes())
.stream_to(tokio::io::stdout())
.await?;
Ok(())
}
```
The route above accepts any `POST` request to the `/debug` path. At most 512KiB
of the incoming is streamed out to `stdout`. If the upload fails, an error
response is returned. The handler above is complete. It really is that simple!
! note: Rocket requires setting limits when reading incoming data.
To aid in preventing DoS attacks, Rocket requires you to specify, as a
[`ByteUnit`](@api/rocket/data/struct.ByteUnit.html), the amount of data you're
willing to accept from the client when `open`ing a data stream. The
[`ToByteUnit`](@api/rocket/data/trait.ToByteUnit.html) trait makes specifying
such a value as idiomatic as `128.kibibytes()`.
## Forms
Forms are one of the most common types of data handled in web applications, and
Rocket makes handling them easy. Rocket supports both `multipart` and
`x-www-form-urlencoded` forms out of the box, enabled by the [`Form`] data guard
and derivable [`FromForm`] trait.
Say your application is processing a form submission for a new todo `Task`. The
form contains two fields: `complete`, a checkbox, and `type`, a text field. You
can easily handle the form request in Rocket as follows:
```rust
# #[macro_use] extern crate rocket;
use rocket::form::Form;
#[derive(FromForm)]
struct Task {
complete: bool,
r#type: String,
}
#[post("/todo", data = "<task>")]
fn new(task: Form<Task>) { /* .. */ }
```
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 [`FromForm`], or equivalently,
[`FromFormField`]. 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 error is returned. As before, a
forward or failure can be caught by using the `Option` and `Result` types:
```rust
# use rocket::{post, form::Form};
# type Task = String;
#[post("/todo", data = "<task>")]
fn new(task: Option<Form<Task>>) { /* .. */ }
```
[`Form`]: @api/rocket/form/struct.Form.html
[`FromForm`]: @api/rocket/form/trait.FromForm.html
### Parsing Strategy
Rocket's `FromForm` parsing is _lenient_ by default: a `Form<T>` will parse
successfully from an incoming form even if it contains extra, duplicate, or
missing fields. Extras or duplicates are ignored -- no validation or parsing of
the fields occurs -- and missing fields are filled with defaults when available.
To change this behavior and make form parsing _strict_, use the
[`Form<Strict<T>>`] data type, which emits errors if there are any extra or
missing fields, irrespective of defaults.
You can use a `Form<Strict<T>>` anywhere you'd use a `Form<T>`. Its generic
parameter is also required to implement `FromForm`. For instance, we can simply
replace `Form<T>` with `Form<Strict<T>>` above to get strict parsing:
```rust
# #[macro_use] extern crate rocket;
use rocket::form::{Form, Strict};
# #[derive(FromForm)] struct Task { complete: bool, description: String, }
#[post("/todo", data = "<task>")]
fn new(task: Form<Strict<Task>>) { /* .. */ }
```
`Strict` can also be used to make individual fields strict while keeping the
overall structure and remaining fields lenient:
```rust
# #[macro_use] extern crate rocket;
# use rocket::form::{Form, Strict};
#[derive(FromForm)]
struct Input {
required: Strict<bool>,
uses_default: bool
}
#[post("/", data = "<input>")]
fn new(input: Form<Input>) { /* .. */ }
```
[`Lenient`] is the _lenient_ analog to `Strict`, which forces parsing to be
lenient. `Form` is lenient by default, so a `Form<Lenient<T>>` is redundant, but
`Lenient` can be used to overwrite a strict parse as lenient:
`Option<Lenient<T>>`.
[`Form<Strict<T>>`]: @api/rocket/form/struct.Strict.html
[`Lenient`]: @api/rocket/form/struct.Lenient.html
### Defaults
A form guard may specify a default value to use when a field is missing. The
default value is used only when parsing is _lenient_. When _strict_, all errors,
including missing fields, are propagated directly.
Some types with defaults include `bool`, which defaults to `false`, useful for
checkboxes, `Option<T>`, which defaults to `None`, and [`form::Result`], which
defaults to `Err(Missing)` or otherwise collects errors in an `Err` of
[`Errors<'_>`]. Defaulting guards can be used just like any other form guard:
```rust
# use rocket::form::FromForm;
use rocket::form::{self, Errors};
#[derive(FromForm)]
struct MyForm<'v> {
maybe_string: Option<&'v str>,
ok_or_error: form::Result<'v, Vec<&'v str>>,
here_or_false: bool,
}
# rocket_guide_tests::assert_form_parses_ok!(MyForm, "");
```
[`Errors<'_>`]: @api/rocket/form/struct.Errors.html
[`form::Result`]: @api/rocket/form/type.Result.html
### 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 one or more `#[field(name = "name")]` or `#[field(name
= uncased("name")]` field annotation. The `uncased` variant case-insensitively
matches field names.
As an example, say that you're writing an application that receives data from an
external service. The external service `POST`s a form with a field named
`first-Name` which you'd like to write as `first_name` in Rust. Such a form
structure can be written as:
```rust
# use rocket::form::FromForm;
#[derive(FromForm)]
struct External {
#[field(name = "first-Name")]
first_name: String
}
```
If you want to accept both `firstName` case-insensitively as well as
`first_name` case-sensitively, you'll need to use two annotations:
```rust
# use rocket::form::FromForm;
#[derive(FromForm)]
struct External {
#[field(name = uncased("firstName"))]
#[field(name = "first_name")]
first_name: String
}
```
This will match any casing of `firstName` including `FirstName`, `firstname`,
`FIRSTname`, and so on, but only match exactly on `first_name`.
If instead you wanted to match any of `first-name`, `first_name` or `firstName`,
in each instance case-insensitively, you would write:
```rust
# use rocket::form::FromForm;
#[derive(FromForm)]
struct External {
#[field(name = uncased("first-name"))]
#[field(name = uncased("first_name"))]
#[field(name = uncased("firstname"))]
first_name: String
}
```
Cased and uncased renamings can be mixed and matched, and any number of
renamings is allowed. Rocket will emit an error at compile-time if field names
conflict, preventing ambiguous parsing at runtime.
### Ad-Hoc Validation
Fields of forms can be easily ad-hoc validated via the `#[field(validate)]`
attribute. As an example, consider a form field `age: u16` which we'd like to
ensure is greater than `21`. The following structure accomplishes this:
```rust
# #[macro_use] extern crate rocket;
#[derive(FromForm)]
struct Person {
#[field(validate = range(21..))]
age: u16
}
```
The expression `range(21..)` is a call to [`form::validate::range`]. Rocket
passes a borrow of the attributed field, here `self.age`, as the first parameter
to the function call. The rest of the fields are pass as written in the
expression.
Any function in the [`form::validate`] module can be called, and other fields of
the form can be passed in by using `self.$field` where `$field` is the name of
the field in the structure. You can also apply more than one validation to a
fiel by using multiple attributes. For example, the following form validates
that the value of the field `confirm` is equal to the value of the field `value`
and that it doesn't contain `no`:
```rust
# #[macro_use] extern crate rocket;
#[derive(FromForm)]
struct Password<'r> {
#[field(name = "password")]
value: &'r str,
#[field(validate = eq(self.value))]
#[field(validate = omits("no"))]
confirm: &'r str,
}
```
[`form::validate`]: @api/rocket/form/validate/index.html
[`form::validate::range`]: @api/rocket/form/validate/fn.range.html
[`form::Result`]: @api/rocket/form/type.Result.html
[`Errors<'_>`]: @api/rocket/form/error/struct.Errors.html
In reality, the expression after `validate =` can be _any_ expression as long as
it evaluates to a value of type `Result<(), Errors<'_>>` (aliased by
[`form::Result`]), where an `Ok` value means that validation was successful while
an `Err` of [`Errors<'_>`] indicates the error(s) that occured. For instance, if
you wanted to implement an ad-hoc Luhn validator for credit-card-like numbers,
you might write:
```rust
# #[macro_use] extern crate rocket;
extern crate time;
use rocket::form::{self, Error};
#[derive(FromForm)]
struct CreditCard {
#[field(validate = luhn(self.cvv, &self.expiration))]
number: u64,
#[field(validate = range(..9999))]
cvv: u16,
expiration: time::Date,
}
fn luhn<'v>(number: &u64, cvv: u16, exp: &time::Date) -> form::Result<'v, ()> {
# let valid = false;
if !valid {
Err(Error::validation("invalid credit card number"))?;
}
Ok(())
}
```
If a field's validation doesn't depend on other fields (validation is _local_),
it is validated prior to those fields that do. For `CreditCard`, `cvv` and
`expiration` will be validated prior to `number`.
### Collections
Rocket's form support allows your application to express _any_ structure with
_any_ level of nesting and collection, eclipsing the expressivity offered by any
other web framework. To parse into these structures, Rocket separates a field's
name into "keys" by the delimiters `.` and `[]`, each of which in turn is
separated into "indices" by `:`. In other words, a name has keys and a key has
indices, each a strict subset of its parent. This is depicted in the example
below with two form fields:
```html
food.bart[bar:foo].blam[0_0][1000]=some-value&another_field=another_val
|-------------------------------| name
|--| |--| |-----| |--| |-| |--| keys
|--| |--| |-| |-| |--| |-| |--| indices
```
Rocket _pushes_ form fields to `FromForm` types as they arrive. The type then
operates on _one_ key (and all of its indices) at a time and _shifts_ to the
next `key`, from left-to-right, before invoking any other `FromForm` types with
the rest of the field. A _shift_ encodes a nested structure while indices allows
for structures that need more than one value to allow indexing.
! note: A `.` after a `[]` is optional.
The form field name `a[b]c` is exactly equivalent to `a[b].c`. Likewise, the
form field name `.a` is equivalent to `a`.
### Nesting
Form structs can be nested:
```rust
use rocket::form::FromForm;
#[derive(FromForm)]
struct MyForm {
owner: Person,
pet: Pet,
}
#[derive(FromForm)]
struct Person {
name: String
}
#[derive(FromForm)]
struct Pet {
name: String,
#[field(validate = eq(true))]
good_pet: bool,
}
```
To parse into a `MyForm`, a form with the following fields must be submitted:
* `owner.name` - string
* `pet.name` - string
* `pet.good_pet` - boolean
Such a form, URL-encoded, may look like:
```rust
# use rocket::form::FromForm;
# use rocket_guide_tests::{assert_form_parses, assert_not_form_parses};
# #[derive(FromForm, Debug, PartialEq)] struct MyForm { owner: Person, pet: Pet, }
# #[derive(FromForm, Debug, PartialEq)] struct Person { name: String }
# #[derive(FromForm, Debug, PartialEq)] struct Pet { name: String, good_pet: bool, }
# assert_form_parses! { MyForm,
"owner.name=Bob&pet.name=Sally&pet.good_pet=on",
# "owner.name=Bob&pet.name=Sally&pet.good_pet=yes",
# "owner.name=Bob&pet.name=Sally&pet.good_pet=on",
# "pet.name=Sally&owner.name=Bob&pet.good_pet=on",
# "pet.name=Sally&pet.good_pet=on&owner.name=Bob",
# =>
// ...which parses as this struct.
MyForm {
owner: Person {
name: "Bob".into()
},
pet: Pet {
name: "Sally".into(),
good_pet: true,
}
}
# };
```
Note that `.` is used to separate each field. Identically, `[]` can be used in
place of or in addition to `.`:
```rust
# use rocket::form::FromForm;
# use rocket_guide_tests::{assert_form_parses, assert_not_form_parses};
# #[derive(FromForm, Debug, PartialEq)] struct MyForm { owner: Person, pet: Pet, }
# #[derive(FromForm, Debug, PartialEq)] struct Person { name: String }
# #[derive(FromForm, Debug, PartialEq)] struct Pet { name: String, good_pet: bool, }
// All of these are identical to the previous...
# assert_form_parses! { MyForm,
"owner[name]=Bob&pet[name]=Sally&pet[good_pet]=on",
"owner[name]=Bob&pet[name]=Sally&pet.good_pet=on",
"owner.name=Bob&pet[name]=Sally&pet.good_pet=on",
"pet[name]=Sally&owner.name=Bob&pet.good_pet=on",
# =>
// ...and thus parse as this struct.
MyForm {
owner: Person {
name: "Bob".into()
},
pet: Pet {
name: "Sally".into(),
good_pet: true,
}
}
# };
```
Any level of nesting is allowed.
### Vectors
A form can also contain sequences:
```rust
# use rocket::form::FromForm;
#[derive(FromForm)]
struct MyForm {
numbers: Vec<usize>,
}
```
To parse into a `MyForm`, a form with the following fields must be submitted:
* `numbers[$k]` - usize (or equivalently, `numbers.$k`)
...where `$k` is the "key" used to determine whether to push the rest of the
field to the last element in the vector or create a new one. If the key is the
same as the previous key seen by the vector, then the field's value is pushed to
the last element. Otherwise, a new element is created. The actual value of `$k`
is irrelevant: it is only used for comparison, has no semantic meaning, and is
not remembered by `Vec`. The special blank key is never equal to any other key.
Consider the following examples.
```rust
# use rocket::form::FromForm;
# use rocket_guide_tests::{assert_form_parses, assert_not_form_parses};
# #[derive(FromForm, PartialEq, Debug)] struct MyForm { numbers: Vec<usize>, }
// These form strings...
# assert_form_parses! { MyForm,
"numbers[]=1&numbers[]=2&numbers[]=3",
"numbers[a]=1&numbers[b]=2&numbers[c]=3",
"numbers[a]=1&numbers[b]=2&numbers[a]=3",
"numbers[]=1&numbers[b]=2&numbers[c]=3",
"numbers.0=1&numbers.1=2&numbers[c]=3",
"numbers=1&numbers=2&numbers=3",
# =>
// ...parse as this struct:
MyForm {
numbers: vec![1 ,2, 3]
}
# };
// These, on the other hand...
# assert_form_parses! { MyForm,
"numbers[0]=1&numbers[0]=2&numbers[]=3",
"numbers[]=1&numbers[b]=3&numbers[b]=2",
# =>
// ...parse as this struct:
MyForm {
numbers: vec![1, 3]
}
# };
```
You might be surprised to see the last example,
`"numbers=1&numbers=2&numbers=3"`, in the first list. This is equivalent to the
previous examples as the "key" seen by the `Vec` (everything after `numbers`) is
empty. Thus, `Vec` pushes to a new `usize` for every field. `usize`, like all
types that implement `FromFormField`, discard duplicate and extra fields when
parsed leniently, keeping only the _first_ field.
### Nesting in Vectors
Any `FromForm` type can appear in a sequence:
```rust
# use rocket::form::FromForm;
#[derive(FromForm)]
struct MyForm {
name: String,
pets: Vec<Pet>,
}
#[derive(FromForm)]
struct Pet {
name: String,
#[field(validate = eq(true))]
good_pet: bool,
}
```
To parse into a `MyForm`, a form with the following fields must be submitted:
* `name` - string
* `pets[$k].name` - string
* `pets[$k].good_pet` - boolean
Examples include:
```rust
# use rocket::form::FromForm;
# use rocket_guide_tests::{assert_form_parses, assert_not_form_parses};
# #[derive(FromForm, Debug, PartialEq)] struct MyForm { name: String, pets: Vec<Pet>, }
# #[derive(FromForm, Debug, PartialEq)] struct Pet { name: String, good_pet: bool, }
// These form strings...
assert_form_parses! { MyForm,
"name=Bob&pets[0].name=Sally&pets[0].good_pet=on",
"name=Bob&pets[sally].name=Sally&pets[sally].good_pet=yes",
# =>
// ...parse as this struct:
MyForm {
name: "Bob".into(),
pets: vec![Pet { name: "Sally".into(), good_pet: true }],
}
# };
// These, on the other hand, fail to parse:
# assert_not_form_parses! { MyForm,
"name=Bob&pets[0].name=Sally&pets[1].good_pet=on",
"name=Bob&pets[].name=Sally&pets[].good_pet=on",
# };
```
### Nested Vectors
Since vectors are `FromForm` themselves, they can appear inside of vectors:
```rust
# use rocket::form::FromForm;
#[derive(FromForm)]
struct MyForm {
v: Vec<Vec<usize>>,
}
```
The rules are exactly the same.
```rust
# use rocket::form::FromForm;
# use rocket_guide_tests::assert_form_parses;
# #[derive(FromForm, Debug, PartialEq)] struct MyForm { v: Vec<Vec<usize>>, }
# assert_form_parses! { MyForm,
"v=1&v=2&v=3" => MyForm { v: vec![vec![1], vec![2], vec![3]] },
"v[][]=1&v[][]=2&v[][]=3" => MyForm { v: vec![vec![1], vec![2], vec![3]] },
"v[0][]=1&v[0][]=2&v[][]=3" => MyForm { v: vec![vec![1, 2], vec![3]] },
"v[][]=1&v[0][]=2&v[0][]=3" => MyForm { v: vec![vec![1], vec![2, 3]] },
"v[0][]=1&v[0][]=2&v[0][]=3" => MyForm { v: vec![vec![1, 2, 3]] },
"v[0][0]=1&v[0][0]=2&v[0][]=3" => MyForm { v: vec![vec![1, 3]] },
"v[0][0]=1&v[0][0]=2&v[0][0]=3" => MyForm { v: vec![vec![1]] },
# };
```
### Maps
A form can also contain maps:
```rust
# use rocket::form::FromForm;
use std::collections::HashMap;
#[derive(FromForm)]
struct MyForm {
ids: HashMap<String, usize>,
}
```
To parse into a `MyForm`, a form with the following fields must be submitted:
* `ids[$string]` - usize (or equivalently, `ids.$string`)
...where `$string` is the "key" used to determine which value in the map to push
the rest of the field to. Unlike with vectors, the key _does_ have a semantic
meaning and _is_ remembered, so ordering of fields is inconsequential: a given
string `$string` always maps to the same element.
As an example, the following are equivalent and all parse to `{ "a" => 1, "b" =>
2 }`:
```rust
# use std::collections::HashMap;
#
# use rocket::form::FromForm;
# use rocket_guide_tests::{map, assert_form_parses};
#
# #[derive(Debug, PartialEq, FromForm)]
# struct MyForm {
# ids: HashMap<String, usize>,
# }
// These form strings...
# assert_form_parses! { MyForm,
"ids[a]=1&ids[b]=2",
"ids[b]=2&ids[a]=1",
"ids[a]=1&ids[a]=2&ids[b]=2",
"ids.a=1&ids.b=2",
# =>
// ...parse as this struct:
MyForm {
ids: map! {
"a" => 1usize,
"b" => 2usize,
}
}
# };
```
Both the key and value of a `HashMap` can be any type that implements
`FromForm`. Consider a value representing another structure:
```rust
# use std::collections::HashMap;
# use rocket::form::FromForm;
#[derive(FromForm)]
struct MyForm {
ids: HashMap<usize, Person>,
}
#[derive(FromForm)]
struct Person {
name: String,
age: usize
}
```
To parse into a `MyForm`, a form with the following fields must be submitted:
* `ids[$usize].name` - string
* `ids[$usize].age` - usize
Examples include:
```rust
# use std::collections::HashMap;
#
# use rocket::form::FromForm;
# use rocket_guide_tests::{map, assert_form_parses};
#
# #[derive(FromForm, Debug, PartialEq)] struct MyForm { ids: HashMap<usize, Person>, }
# #[derive(FromForm, Debug, PartialEq)] struct Person { name: String, age: usize }
// These form strings...
# assert_form_parses! { MyForm,
"ids[0]name=Bob&ids[0]age=3&ids[1]name=Sally&ids[1]age=10",
"ids[0]name=Bob&ids[1]age=10&ids[1]name=Sally&ids[0]age=3",
"ids[0]name=Bob&ids[1]name=Sally&ids[0]age=3&ids[1]age=10",
# =>
// ...which parse as this struct:
MyForm {
ids: map! {
0usize => Person { name: "Bob".into(), age: 3 },
1usize => Person { name: "Sally".into(), age: 10 },
}
}
# };
```
Now consider the following structure where both the key and value represent
structures:
```rust
# use std::collections::HashMap;
# use rocket::form::FromForm;
#[derive(FromForm)]
struct MyForm {
m: HashMap<Person, Pet>,
}
#[derive(FromForm, PartialEq, Eq, Hash)]
struct Person {
name: String,
age: usize
}
#[derive(FromForm)]
struct Pet {
wags: bool
}
```
! warning: The `HashMap` key type, here `Person`, must implement `Eq + Hash`.
Since the key is a collection, here `Person`, it must be built up from multiple
fields. This requires being able to specify via the form field name that the
field's value corresponds to a key in the map. The is done with the syntax
`k:$key` which indicates that the field corresponds to the `k`ey named `$key`.
Thus, to parse into a `MyForm`, a form with the following fields must be
submitted:
* `m[k:$key].name` - string
* `m[k:$key].age` - usize
* `m[$key].wags` or `m[v:$key].wags` - boolean
! note: The syntax `v:$key` also exists.
The shorthand `m[$key]` is equivalent to `m[v:$key]`.
Note that `$key` can be _anything_: it is simply a symbolic identifier for a
key/value pair in the map and has no bearing on the actual values that will be
parsed into the map.
Examples include:
```rust
# use std::collections::HashMap;
#
# use rocket::form::FromForm;
# use rocket_guide_tests::{map, assert_form_parses};
#
# #[derive(FromForm, Debug, PartialEq)] struct MyForm { m: HashMap<Person, Pet>, }
# #[derive(FromForm, Debug, PartialEq, Eq, Hash)] struct Person { name: String, age: usize }
# #[derive(FromForm, Debug, PartialEq)] struct Pet { wags: bool }
// These form strings...
# assert_form_parses! { MyForm,
"m[k:alice]name=Alice&m[k:alice]age=30&m[v:alice].wags=no",
"m[k:alice]name=Alice&m[k:alice]age=30&m[alice].wags=no",
"m[k:123]name=Alice&m[k:123]age=30&m[123].wags=no",
# =>
// ...which parse as this struct:
MyForm {
m: map! {
Person { name: "Alice".into(), age: 30 } => Pet { wags: false }
}
}
# };
// While this longer form string...
# assert_form_parses! { MyForm,
"m[k:a]name=Alice&m[k:a]age=40&m[a].wags=no&\
m[k:b]name=Bob&m[k:b]age=72&m[b]wags=yes&\
m[k:cat]name=Katie&m[k:cat]age=12&m[cat]wags=yes",
# =>
// ...parses as this struct:
MyForm {
m: map! {
Person { name: "Alice".into(), age: 40 } => Pet { wags: false },
Person { name: "Bob".into(), age: 72 } => Pet { wags: true },
Person { name: "Katie".into(), age: 12 } => Pet { wags: true },
}
}
# };
```
### Arbitrary Collections
_Any_ collection can be expressed with any level of arbitrary nesting, maps, and
sequences. Consider the extravagently contrived type:
```rust
use std::collections::{BTreeMap, HashMap};
# use rocket::form::FromForm;
#[derive(FromForm, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
struct Person {
name: String,
age: usize
}
# type Foo =
HashMap<Vec<BTreeMap<Person, usize>>, HashMap<usize, Person>>
# ;
# /*
|-[k:$k1]-----------|------|------| |-[$k1]-----------------|
|---[$i]-------|------|------| |-[k:$j]*|
|-[k:$k2]|------| ~~[$j]~~|name*|
|-name*| ~~[$j]~~|age-*|
|-age*-|
|~~~~~~~~~~~~~~~|v:$k2*|
# */
```
! warning: The `BTreeMap` key type, here `Person`, must implement `Ord`.
As illustrated above with `*` marking terminals, we need the following form
fields for this structure:
* `[k:$k1][$i][k:$k2]name` - string
* `[k:$k1][$i][k:$k2]age` - usize
* `[k:$k1][$i][$k2]` - usize
* `[$k1][k:$j]` - usize
* `[$k1][$j]name` - string
* `[$k1][$j]age` - string
Where we have the following symbolic keys:
* `$k1`: symbolic name of the top-level key
* `$i`: symbolic name of the vector index
* `$k2`: symbolic name of the sub-level (`BTreeMap`) key
* `$j`: symbolic name and/or value top-level value's key
```rust
# use std::collections::BTreeMap;
# use std::collections::HashMap;
#
# use rocket::form::FromForm;
# use rocket_guide_tests::{map, bmap, assert_form_parses};
# #[derive(FromForm, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
# struct Person { name: String, age: usize }
type Foo = HashMap<Vec<BTreeMap<Person, usize>>, HashMap<usize, Person>>;
// This (long, contrived) form string...
# assert_form_parses! { Foo,
"[k:top_key][i][k:sub_key]name=Bobert&\
[k:top_key][i][k:sub_key]age=22&\
[k:top_key][i][sub_key]=1337&\
[top_key][7]name=Builder&\
[top_key][7]age=99",
// We could also set the top-level value's key explicitly:
// [top_key][k:7]=7
# "[k:top_key][i][k:sub_key]name=Bobert&\
# [k:top_key][i][k:sub_key]age=22&\
# [top_key][k:7]=7&\
# [k:top_key][i][sub_key]=1337&\
# [top_key][7]name=Builder&\
# [top_key][7]age=99",
# =>
// ...parses as this (long, contrived) map:
map! {
vec![bmap! {
Person { name: "Bobert".into(), age: 22 } => 1337usize,
}]
=>
map! {
7usize => Person { name: "Builder".into(), age: 99 }
}
}
# };
```
### Context
The [`Contextual`] type acts as a proxy for any form type, recording all of the
submitted form values and produced errors and associating them with their
corresponding field name. `Contextual` is particularly useful to render a form
with previously submitted values and render errors associated with a form input.
To retrieve the context for a form, use `Form<Contextual<'_, T>>` as a data
guard, where `T` implements `FromForm`. The `context` field contains the form's
[`Context`]:
```rust
# use rocket::post;
# type T = String;
use rocket::form::{Form, Contextual};
#[post("/submit", data = "<form>")]
fn submit(form: Form<Contextual<'_, T>>) {
if let Some(ref value) = form.value {
// The form parsed successfully. `value` is the `T`.
}
// In all cases, `form.context` contains the `Context`.
// We can retrieve raw field values and errors.
let raw_id_value = form.context.value("id");
let id_errors = form.context.errors("id");
}
```
`Context` serializes as a map, so it can be rendered in templates that require
`Serialize` types. See
[`Context`](@api/rocket/form/struct.Context.html#Serialization) for details
about its serialization format. The [forms example], too, makes use of form
contexts, as well as every other forms feature.
[`Contextual`]: @api/rocket/form/struct.Contextual.html
[`Context`]: @api/rocket/form/struct.Context.html
[forms example]: @example/forms
## Query Strings
Query strings are URL-encoded forms that appear in the URL of a request. Query
parameters are declared like path parameters but otherwise handled like regular
URL-encoded form fields. The table below summarizes the analogy:
| Path Synax | Query Syntax | Path Type Bound | Query Type Bound |
|-------------|--------------|------------------|------------------|
| `<param>` | `<param>` | [`FromParam`] | [`FromForm`] |
| `<param..>` | `<param..>` | [`FromSegments`] | [`FromForm`] |
| `static` | `static` | N/A | N/A |
Because dynamic parameters are form types, they can be single values,
collections, nested collections, or anything in between, just like any other
form field.
### Static Parameters
A request matches a route _iff_ its query string contains all of the static
parameters in the route's query string. A route with a static parameter `param`
(any UTF-8 text string) in a query will only match requests with that exact path
segment in its query string.
! note: This is truly an _iff_!
Only the static parameters in query route string affect routing. Dynamic
parameters are allowed to be missing by default.
For example, the route below will match requests with path `/` and _at least_
the query segments `hello` and `cat=♥`:
```rust
# #[macro_use] extern crate rocket;
#[get("/?hello&cat=♥")]
fn cats() -> &'static str {
"Hello, kittens!"
}
// The following GET requests match `cats`. `%E2%99%A5` is encoded `♥`.
# let status = rocket_guide_tests::client(routes![cats]).get(
"/?cat=%E2%99%A5&hello"
# ).dispatch().status();
# assert_eq!(status, rocket::http::Status::Ok);
# let status = rocket_guide_tests::client(routes![cats]).get(
"/?hello&cat=%E2%99%A5"
# ).dispatch().status();
# assert_eq!(status, rocket::http::Status::Ok);
# let status = rocket_guide_tests::client(routes![cats]).get(
"/?dogs=amazing&hello&there&cat=%E2%99%A5"
# ).dispatch().status();
# assert_eq!(status, rocket::http::Status::Ok);
```
### Dynamic Parameters
A single dynamic parameter of `<param>` acts identically to a form field
declared as `param`. In particular, Rocket will expect the query form to contain
a field with key `param` and push the shifted field to the `param` type. As with
forms, default values are used when parsing fails. The example below illustrates
this with a single value `name`, a collection `color`, a nested form `person`,
and an `other` value that will default to `None`:
```rust
# #[macro_use] extern crate rocket;
#[derive(Debug, PartialEq, FromFormField)]
enum Color {
Red,
Blue,
Green
}
#[derive(Debug, PartialEq, FromForm)]
struct Pet<'r> {
name: &'r str,
age: usize,
}
#[derive(Debug, PartialEq, FromForm)]
struct Person<'r> {
pet: Pet<'r>,
}
#[get("/?<name>&<color>&<person>&<other>")]
fn hello(name: &str, color: Vec<Color>, person: Person<'_>, other: Option<usize>) {
assert_eq!(name, "George");
assert_eq!(color, [Color::Red, Color::Green, Color::Green, Color::Blue]);
assert_eq!(other, None);
assert_eq!(person, Person {
pet: Pet { name: "Fi Fo Alex", age: 1 }
});
}
// A request with these query segments matches as above.
# rocket_guide_tests::client(routes![hello]).get("/?\
color=reg&\
color=green&\
person.pet.name=Fi+Fo+Alex&\
color=green&\
person.pet.age=1\
color=blue&\
extra=yes\
# ").dispatch();
```
Note that, like forms, parsing is field-ordering insensitive and lenient by
default.
### Trailing Parameter
A trailing dynamic parameter of `<param..>` collects all of the query segments
that don't otherwise match a declared static or dynamic parameter. In other
words, the otherwise unmatched segments are pushed, unshifted, to the
`<param..>` type:
```rust
# #[macro_use] extern crate rocket;
use rocket::form::Form;
#[derive(FromForm)]
struct User<'r> {
name: &'r str,
active: bool,
}
#[get("/?hello&<id>&<user..>")]
fn user(id: usize, user: User<'_>) {
assert_eq!(id, 1337);
assert_eq!(user.name, "Bob Smith");
assert_eq!(user.active, true);
}
// A request with these query segments matches as above.
# rocket_guide_tests::client(routes![user]).get("/?\
name=Bob+Smith&\
id=1337\
active=yes\
# ").dispatch();
```
## Error Catchers
Application processing is fallible. Errors arise from the following sources:
* A failing guard.
* A failing responder.
* A routing failure.
If any of these occur, Rocket returns an error to the client. To generate the
error, Rocket invokes the _catcher_ corresponding to the error's status code.
Catchers are similar to routes except in that:
1. Catchers are only invoked on error conditions.
2. Catchers are declared with the `catch` attribute.
3. Catchers are _registered_ with [`register()`] instead of [`mount()`].
4. Any modifications to cookies are cleared before a catcher is invoked.
5. Error catchers cannot invoke guards.
6. Error catchers should not fail to produce a response.
To declare a catcher for a given status code, use the [`catch`] attribute, which
takes a single integer corresponding to the HTTP status code to catch. For
instance, to declare a catcher for `404 Not Found` errors, you'd write:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
use rocket::Request;
#[catch(404)]
fn not_found(req: &Request) { /* .. */ }
```
Catchers may take zero, one, or two arguments. If the catcher takes one
argument, it must be of type [`&Request`]. It it takes two, they must be of type
[`Status`] and [`&Request`], in that order. As with routes, the return type must
implement `Responder`. A concrete implementation may look like:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
# use rocket::Request;
#[catch(404)]
fn not_found(req: &Request) -> String {
format!("Sorry, '{}' is not a valid path.", req.uri())
}
```
Also as with routes, Rocket needs to know about a catcher before it is used to
handle errors. The process, known as "registering" a catcher, is similar to
mounting a route: call the [`register()`] method with a list of catchers via the
[`catchers!`] macro. The invocation to add the **404** catcher declared above
looks like:
```rust
# #[macro_use] extern crate rocket;
# use rocket::Request;
# #[catch(404)] fn not_found(req: &Request) { /* .. */ }
fn main() {
rocket::ignite().register(catchers![not_found]);
}
```
### Default Catchers
If no catcher for a given status code has been registered, Rocket calls the
_default_ catcher. Rocket provides a default catcher for all applications
automatically, so providing one is usually unnecessary. Rocket's built-in
default catcher can handle all errors. It produces HTML or JSON, depending on
the value of the `Accept` header. As such, a default catcher, or catchers in
general, only need to be registered if an error needs to be handled in a custom
fashion.
Declaring a default catcher is done with `#[catch(default)]`:
```rust
# #[macro_use] extern crate rocket;
# fn main() {}
use rocket::Request;
use rocket::http::Status;
#[catch(default)]
fn default_catcher(status: Status, request: &Request) { /* .. */ }
```
It must similarly be registered with [`register()`].
The [error catcher example](@example/errors) illustrates their use in full,
while the [`Catcher`] API documentation provides further details.
[`catch`]: @api/rocket/attr.catch.html
[`register()`]: @api/rocket/struct.Rocket.html#method.register
[`mount()`]: @api/rocket/struct.Rocket.html#method.mount
[`catchers!`]: @api/rocket/macro.catchers.html
[`&Request`]: @api/rocket/struct.Request.html
[`Status`]: @api/rocket/http/struct.Status.html
[`Catcher`]: @api/rocket/catcher/struct.Catcher.html