Update guide in full for current 0.4.0-dev.

This commit is contained in:
Sergio Benitez 2018-10-22 14:47:35 -07:00
parent 9ef0b731c8
commit 3903ffdb47
13 changed files with 591 additions and 314 deletions

View File

@ -5,38 +5,50 @@ being a more flexible, friendly medley of [Rails](http://rubyonrails.org),
[Flask](http://flask.pocoo.org/),
[Bottle](http://bottlepy.org/docs/dev/index.html), and
[Yesod](http://www.yesodweb.com/). We prefer to think of Rocket as something
new. Rocket aims to be fast, easy, and flexible. It also aims to be _fun_, and
new. Rocket aims to be fast, easy, and flexible while offerring guaranteed
safety and security where it can. Importantly, Rocket also aims to be _fun_, and
it accomplishes this by ensuring that you write as little code as needed to
accomplish your task. This guide introduces you to the core, intermediate, and
advanced concepts of Rocket. After reading this guide, you should find yourself
being _very_ productive with Rocket.
accomplish your task.
This guide introduces you to the core, intermediate, and advanced concepts of
Rocket. After reading this guide, you should find yourself being very
productive with Rocket.
## Audience
Readers are assumed to have a good grasp of the Rust programming language.
Readers new to Rust are encouraged to read the [Rust
Book](https://doc.rust-lang.org/book/). This guide also assumes a basic
understanding of web application fundamentals, such as routing and HTTP.
understanding of web application fundamentals, such as routing and HTTP. Mozilla
provides a good overview of these concepts in their [MDN web docs].
[MDN web docs]: https://developer.mozilla.org/en-US/docs/Web/HTTP
## Foreword
Rocket's design is centered around three core philosophies:
* **Function declaration and parameter types should contain all the necessary
information to validate and process a request.** This immediately prohibits
APIs where request state is retrieved from a global context. As a result,
request handling is **self-contained** in Rocket: handlers are regular
functions with regular arguments.
* **Security, correctness, and developer experience are paramount.**
* **All request handling information should be typed.** Because the web and
HTTP are themselves untyped (or _stringly_ typed, as some call it), this
means that something or someone has to convert strings to native types.
Rocket does this for you with zero programming overhead.
The path of least resistance should lead you to the most secure, correct web
application, though security and correctness should not come at the cost of
a degraded developer experience. Rocket is easy to use while taking great
measures to ensure that your application is secure and correct without
cognitive overhead.
* **Decisions should not be forced.** Templates, serialization, sessions, and
just about everything else are all pluggable, optional components. While
Rocket has official support and libraries for each of these, they are
completely optional and swappable.
* **All request handling information should be typed and self-contained.**
Because the web and HTTP are themselves untyped (or _stringly_ typed, as
some call it), this means that something or someone has to convert strings
to native types. Rocket does this for you with zero programming overhead.
What's more, Rocket's request handling is **self-contained** with zero
global state: handlers are regular functions with regular arguments.
* **Decisions should not be forced.**
Templates, serialization, sessions, and just about everything else are all
pluggable, optional components. While Rocket has official support and
libraries for each of these, they are completely optional and swappable.
These three ideas dictate Rocket's interface, and you will find all of them
embedded in Rocket's core features.

View File

@ -22,3 +22,11 @@ cargo run
There are numerous examples in the `examples/` directory. They can all be run
with `cargo run`.
! note
The examples' `Cargo.toml` files will point to the locally cloned `rocket`
libraries. When copying the examples for your own use, you should modify the
`Cargo.toml` files as explained in the [Getting Started] guide.
[Getting Started]: ../getting-started

View File

@ -28,15 +28,13 @@ Rocket project by running the following command in the directory:
rustup override set nightly
```
### Minimum Nightly
! warning: Rocket requires the _latest_ version of Rust nightly.
Rocket always requires the _latest_ version of Rust nightly. If your Rocket
application suddenly stops building, ensure you're using the latest version of
Rust nightly and Rocket by updating your toolchain and dependencies with:
If your Rocket application suddenly stops building, ensure you're using the
latest version of Rust nightly and Rocket by updating your toolchain and
dependencies with:
```sh
rustup update && cargo update
```
`rustup update && cargo update`
## Hello, world!
@ -48,13 +46,11 @@ cargo new hello-rocket --bin
cd hello-rocket
```
Now, add Rocket and its code generation facilities as dependencies of your
project by ensuring your `Cargo.toml` contains the following:
Now, add Rocket as a dependency in your `Cargo.toml`:
```
[dependencies]
rocket = "0.3.6"
rocket_codegen = "0.3.6"
rocket = "0.4.0-dev"
```
Modify `src/main.rs` so that it contains the code for the Rocket `Hello, world!`
@ -85,13 +81,21 @@ run`. You should see the following:
=> address: localhost
=> port: 8000
=> log: normal
=> workers: [core count * 2]
=> workers: 24
=> secret key: generated
=> limits: forms = 32KiB
=> keep-alive: 5s
=> tls: disabled
🛰 Mounting '/':
=> GET /
=> GET / (hello)
🚀 Rocket has launched from http://localhost:8000
```
Visit `http://localhost:8000` to see your first Rocket application in action!
! tip: Don't like colors or emoji?
You can disable colors and emoji by setting the `ROCKET_CLI_COLORS`
environment variable to `0` or `off` when running a Rocket binary:
`ROCKET_CLI_COLORS=off cargo run`

View File

@ -1,10 +1,10 @@
# Overview
Rocket provides primitives to build web servers and applications with Rust: the
rest is up to you. In short, Rocket provides routing, pre-processing of
requests, and post-processing of responses. Your application code instructs
Rocket on what to pre-process and post-process and fills the gaps between
pre-processing and post-processing.
Rocket provides primitives to build web servers and applications with Rust:
Rocket provides routing, pre-processing of requests, and post-processing of
responses; the rest is up to you. Your application code instructs Rocket on what
to pre-process and post-process and fills the gaps between pre-processing and
post-processing.
## Lifecycle
@ -76,27 +76,24 @@ constructing routes.
## Mounting
Before Rocket can dispatch requests to a route, the route needs to be _mounted_.
Mounting a route is like namespacing it. Routes are mounted via the `mount`
method on a `Rocket` instance. A `Rocket` instance is typically created with the
`rocket::ignite()` static method.
The `mount` method takes:
1. A path to namespace a list of routes under,
2. A list of route handlers through the `routes!` macro, tying Rocket's code
generation to your application.
For instance, to mount the `world` route we declared above, we can write the
following:
Before Rocket can dispatch requests to a route, the route needs to be _mounted_:
```rust
rocket::ignite().mount("/hello", routes![world]);
fn main() {
rocket::ignite().mount("/hello", routes![world]);
}
```
The `mount` method takes as input:
1. A _base_ path to namespace a list of routes under.
2. A list of routes via the `routes!` macro.
This creates a new `Rocket` instance via the `ignite` function and mounts the
`world` route to the `"/hello"` path. As a result, `GET` requests to the
`"/hello/world"` path will be directed to the `world` function.
`world` route to the `"/hello"` path, making Rocket aware of the route. `GET`
requests to `"/hello/world"` will be directed to the `world` function.
! note: In many cases, the base path will simply be `"/"`.
### Namespacing
@ -121,7 +118,7 @@ fn main() {
This occurs because the `routes!` macro implicitly converts the route's name
into the name of a structure generated by Rocket's code generation. The solution
is to name the route by a module path instead:
is to refer to the route using a namespaced path instead:
```rust
rocket::ignite().mount("/hello", routes![other::world]);
@ -153,10 +150,10 @@ fn main() {
```
Note the `#![feature]` line: this tells Rust that we're opting in to compiler
features vailable in the nightly release channel. This line must be in the crate
root, typically `main.rs`. We've also imported the `rocket` crate and all of its
macros into our namespace via `#[macro_use] extern crate rocket`. Finally, we
call the `launch` method in the `main` function.
features available in the nightly release channel. This line **must** be in the
crate root, typically `main.rs`. We've also imported the `rocket` crate and all
of its macros into our namespace via `#[macro_use] extern crate rocket`.
Finally, we call the `launch` method in the `main` function.
Running the application, the console shows:
@ -165,21 +162,20 @@ Running the application, the console shows:
=> address: localhost
=> port: 8000
=> log: normal
=> workers: [logical cores * 2]
=> workers: 24
=> secret key: generated
=> limits: forms = 32KiB
=> keep-alive: 5s
=> tls: disabled
🛰 Mounting '/hello':
=> GET /hello/world
🛰 Mounting '/':
=> GET / (hello)
🚀 Rocket has launched from http://localhost:8000
```
If we visit `localhost:8000/hello/world`, we see `Hello, world!`, exactly as
we expected.
If we visit `localhost:8000/hello/world`, we see `Hello, world!`, exactly as we
expected.
A version of this example's complete crate, ready to `cargo run`, can be found
on
[GitHub](@example/hello_world).
You can find dozens of other complete examples, spanning all of Rocket's
features, in the [GitHub examples
on [GitHub](@example/hello_world). You can find dozens of other complete
examples, spanning all of Rocket's features, in the [GitHub examples
directory](@example/).

View File

@ -1,6 +1,6 @@
# Requests
Together, a route's attribute and function signature specify what must be true
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:
@ -15,8 +15,8 @@ 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 _many_ dynamic path segments.
* The type of incoming data.
* 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.
@ -26,6 +26,8 @@ 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_codegen/attr.route.html
## Methods
A Rocket route attribute can be any one of `get`, `put`, `post`, `delete`,
@ -38,7 +40,7 @@ to the root path:
```
The grammar for these attributes is defined formally in the
[`rocket_codegen`](@api/rocket_codegen/) API docs.
[`rocket_codegen`](@api/rocket_codegen/attr.route.html) API docs.
### HEAD Requests
@ -46,20 +48,20 @@ 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 handles.
your application explicitly handles.
### Reinterpreting
Because browsers can only send `GET` and `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**
`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 Segments
## 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,
@ -79,10 +81,10 @@ 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. Rocket implements `FromParam` for many of the standard library types, as
well as a few special Rocket types. For the full list of supplied
implementations, see the [`FromParam` API docs]. Here's a more complete route to
illustrate varied usage:
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
#[get("/hello/<name>/<age>/<cool>")]
@ -98,28 +100,70 @@ fn hello(name: String, age: u8, cool: bool) -> String {
[`FromParam`]: @api/rocket/request/trait.FromParam.html
[`FromParam` API docs]: @api/rocket/request/trait.FromParam.html
### Raw Strings
! note: Rocket types _raw_ strings separately from decoded strings.
You may have noticed an unfamiliar [`RawStr`] type in the code example above.
This is a special type, provided by Rocket, that represents an unsanitized,
unvalidated, and undecoded raw string from an HTTP message. It exists to
separate validated string inputs, represented by types such as `String`, `&str`,
and `Cow<str>` types, from unvalidated inputs, represented by `&RawStr`. It
provides helpful methods to convert the unvalidated string into a validated one.
You may have noticed an unfamiliar [`RawStr`] type in the code example above.
This is a special type, provided by Rocket, that represents an unsanitized,
unvalidated, and undecoded raw string from an HTTP message. It exists to
separate validated string inputs, represented by types such as `String`,
`&str`, and `Cow<str>`, from unvalidated inputs, represented by `&RawStr`. It
also provides helpful methods to convert the unvalidated string into a
validated one.
Because `&RawStr` implements [`FromParam`], it can be used as the type of a
dynamic segment, as in the example above. When used as the type of a dynamic
segment, a `RawStr` points to a potentially undecoded string. By contrast, a
`String` is guaranteed to be decoded. Which you should use depends on whether
you want direct but potentially unsafe access to the string (`&RawStr`), or safe
access to the string at the cost of an allocation (`String`).
Because `&RawStr` implements [`FromParam`], it can be used as the type of a
dynamic segment, as in the example above, where the value refers to a
potentially undecoded string. By contrast, a `String` is guaranteed to be
decoded. Which you should use depends on whether you want direct but
potentially unsafe access to the string (`&RawStr`), or safe access to the
string at the cost of an allocation (`String`).
[`RawStr`]: @api/rocket/http/struct.RawStr.html
[`RawStr`]: @api/rocket/http/struct.RawStr.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 std::path::PathBuf;
#[get("/page/<path..>")]
fn get_page(path: PathBuf) -> T { ... }
```
The path after `/page/` will be available in the `path` parameter. The
`FromSegments` implementation for `PathBuf` ensures that `path` cannot lead to
[path traversal attacks](https://www.owasp.org/index.php/Path_Traversal). With
this, a safe and secure static file server can be implemented in 4 lines:
```rust
#[get("/<file..>")]
fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("static/").join(file)).ok()
}
```
! 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 the last
example:
Let's take a closer look at the route attribute and signature pair from a
previous example:
```rust
#[get("/hello/<name>/<age>/<cool>")]
@ -133,9 +177,9 @@ 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 -4 to -1, detailed in the next section, for all routes, but a
route's rank can also be manually set with the `rank` attribute. To illustrate,
consider the following routes:
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
#[get("/user/<id>")]
@ -168,6 +212,11 @@ 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`
@ -181,89 +230,79 @@ precedence) while routes with dynamic paths and without query strings have
higher ranks (lower precedence). The table below describes the default ranking
of a route given its properties.
| static path | query string | rank | example |
| ------------- | -------------- | ------ | ------------------- |
| yes | yes | -4 | `/hello?world=true` |
| yes | no | -3 | `/hello` |
| no | yes | -2 | `/<hi>?world=true` |
| no | no | -1 | `/<hi>` |
| static path | query | rank | example |
|-------------|---------------|------|---------------------|
| yes | partly static | -6 | `/hello?world=true` |
| yes | fully dynamic | -5 | `/hello/?<world>` |
| yes | none | -4 | `/hello` |
| no | partly static | -3 | `/<hi>?world=true` |
| no | fully dynamic | -2 | `/<hi>?<world>` |
| no | none | -1 | `/<hi>` |
## Multiple Segments
## Query Strings
You can also match against multiple segments by using `<param..>` in a route
path. The type of such parameters, known as _segments_ parameters, must
implement [`FromSegments`]. Segments parameters must be the final component of a
path: any text after a segments parameter will result in a compile-time error.
As an example, the following route matches against all paths that begin with
`/page/`:
Query segments can be declared static or dynamic in much the same way as path
segments:
```rust
#[get("/page/<path..>")]
fn get_page(path: PathBuf) -> T { ... }
```
The path after `/page/` will be available in the `path` parameter. The
`FromSegments` implementation for `PathBuf` ensures that `path` cannot lead to
[path traversal attacks](https://www.owasp.org/index.php/Path_Traversal). With
this, a safe and secure static file server can be implemented in 4 lines:
```rust
#[get("/<file..>")]
fn files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("static/").join(file)).ok()
#[get("/hello?wave&<name>")]
fn hello(name: &RawStr) -> String {
format!("Hello, {}!", name.as_str())
}
```
[`FromSegments`]: @api/rocket/request/trait.FromSegments.html
The `hello` route above matches any `GET` request to `/hello` that has at least
one query key of `name` and a query segment of `wave` in any order, ignoring any
extra query segments. The value of the `name` query parameter is used as the
value of the `name` function argument. For instance, a request to
`/hello?wave&name=John` would return `Hello, John!`. Other requests that would
result in the same response include:
## Format
* `/hello?name=John&wave` (reordered)
* `/hello?name=John&wave&id=123` (extra segments)
* `/hello?id=123&name=John&wave` (reordered, extra segments)
* `/hello?name=Bob&name=John&wave` (last value taken)
A route can specify the data format it is willing to accept or respond with
using the `format` route parameter. The value of the parameter is a string
identifying an HTTP media type. For instance, for JSON data, the string
`application/json` can be used.
Any number of dynamic query segments are allowed. A query segment can be of any
type, including your own, as long as the type implements the [`FromFormValue`]
trait.
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.
[`FromFormValue`]: @api/rocket/request/trait.FromFormValue.html
As an example, consider the following route:
### Multiple Segments
As with paths, you can also match against multiple segments in a query by using
`<param..>`. The type of such parameters, known as _query guards_, must
implement the [`FromQuery`] trait. Query guards must be the final component of a
query: any text after a query parameter will result in a compile-time error.
A query guard validates all otherwise unmatched (by static or dynamic query
parameters) query segments. While you can implement [`FromQuery`] yourself, most
use cases will be handled by using the [`Form`] or [`LenientForm`] query guard.
The [Forms](#forms) section explains using these types in detail. In short,
these types allow you to use a structure with named fields to automatically
validate query/form parameters:
```rust
#[post("/user", format = "application/json", data = "<user>")]
fn new_user(user: Json<User>) -> T { ... }
use rocket::request::Form;
#[derive(FromForm)]
struct User {
name: String,
account: usize,
}
#[get("/item?<id>&<user..>")]
fn item(id: usize, user: Form<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.
For a request to `/item?id=100&name=sandal&account=400`, the `item` route above
sets `id` to `100` and `user` to `User { name: "sandal", account: 400 }`.
When a route indicates a non-payload-supporting method (`HEAD`, `OPTIONS`, and,
these purposes, `GET`) 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.
For more query handling examples, see [the `query_params`
example](@example/query_params).
As an example, consider the following route:
```rust
#[get("/user/<id>", format = "json")]
fn user(id: usize) -> Json<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
[`FromQuery`]: @api/rocket/request/trait.FromQuery.html
## Request Guards
@ -280,9 +319,9 @@ invoke the [`FromRequest`] implementation for request guards before calling the
handler. Rocket only dispatches requests to a handler when all of its guards
pass.
As an example, the following dummy handler makes use of three request guards,
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. This is why `param` is not a request guard.
named in the route attribute.
```rust
#[get("/<param>")]
@ -316,6 +355,46 @@ authenticates an administrator using incoming cookies. Then, any handler with an
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
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.
@ -482,13 +561,60 @@ guards:
fn good(custom: Custom, cookies: Cookies) { .. }
```
## 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` of 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
#[post("/user", format = "application/json", data = "<user>")]
fn new_user(user: Json<User>) -> T { ... }
```
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 (`HEAD`, `OPTIONS`, and,
these purposes, `GET`) 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
#[get("/user/<id>", format = "json")]
fn user(id: usize) -> Json<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
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`:
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
#[post("/", data = "<input>")]
@ -501,11 +627,11 @@ 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:
Forms are one of the most common types 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:
```rust
#[derive(FromForm)]
@ -518,21 +644,23 @@ struct Task {
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:
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:
```rust
#[post("/todo", data = "<task>")]
fn new(task: Option<Form<Task>>) -> String { ... }
```
[`Form`]: @api/rocket/request/struct.Form.html
[`FromForm`]: @api/rocket/request/trait.FromForm.html
[`FromFormValue`]: @api/rocket/request/trait.FromFormValue.html
@ -629,14 +757,30 @@ struct Person {
}
```
The [forms validation](@example/form_validation)
and [forms kitchen sink](@example/form_kitchen_sink)
examples on GitHub provide further illustrations.
The `FromFormValue` trait can also be derived for enums with nullary fields:
```rust
#[derive(FromFormValue)]
enum MyValue {
First,
Second,
Third,
}
```
The derive generates an implementation of the `FromFormValue` trait for the
decorated enum. The implementation returns successfully when the form value
matches, case insensitively, the stringified version of a variant's name,
returning an instance of said variant.
The [form validation](@example/form_validation) and [form kitchen
sink](@example/form_kitchen_sink) examples provide further illustrations.
### JSON
Handling JSON data is no harder: simply use the
[`Json`](@api/rocket_contrib/json/struct.Json.html) type:
[`Json`](@api/rocket_contrib/json/struct.Json.html) type from
[`rocket_contrib`]:
```rust
#[derive(Deserialize)]
@ -676,74 +820,27 @@ 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](@example/raw_upload) for the full crate.
## Query Strings
! warning: You should _always_ set limits when reading incoming data.
Query strings are handled just like forms. A query string can be parsed into any
structure that implements the `FromForm` trait. They are matched against by
appending a `?` to the path followed by a static query string or a dynamic
parameter `<param>`.
To prevent DoS attacks, you should limit the amount of data you're willing to
accept. The [`take()`] reader adapter makes doing this easy:
`data.open().take(LIMIT)`.
For instance, say you change your mind and decide to use query strings instead
of `POST` forms for new todo tasks in the previous forms example, reproduced
below:
```rust
#[derive(FromForm)]
struct Task { .. }
#[post("/todo", data = "<task>")]
fn new(task: Form<Task>) -> String { ... }
```
Rocket makes the transition simple: simply declare `<task>` as a query parameter
as follows:
```rust
#[get("/todo?<task>")]
fn new(task: Task) -> String { ... }
```
Rocket will parse the query string into the `Task` structure automatically by
matching the structure field names to the query parameters. If the parse fails,
the request is forwarded to the next matching route. Parse failures can be
captured on a per-field or per-form basis.
To catch failures on a per-field basis, use a type of `Option` or `Result` for
the given field:
```rust
#[derive(FromForm)]
struct Task<'r> {
description: Result<String, &'r RawStr>,
complete: Option<bool>
}
```
To catch failures on a per-form basis, change the type of the query string
target to either `Option` or `Result`:
```rust
#[get("/todo?<task>")]
fn new(task: Option<Task>) { ... }
```
For a concrete illustration on how to handle query parameters, see [the
`query_params`
example](@example/query_params).
[`take()`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.take
## Error Catchers
Routing may fail for a variety of reasons. These include:
* A [request guard](#request-guards) returns `Failure`.
* A guard fails.
* A handler returns a [`Responder`](../responses/#responder) that fails.
* No matching route was found.
* No routes matched.
If any of these conditions occur, Rocket returns an error to the client. To do
so, Rocket invokes the _catcher_ corresponding to the error's status code. A
catcher is like a route, except it only handles errors. Rocket provides default
catchers for all of the standard HTTP error codes. To override a default
catcher, or declare a catcher for a custom status code, use the `catch`
catcher, or declare a catcher for a custom 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:
@ -765,8 +862,8 @@ fn not_found(req: &Request) -> String {
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
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
@ -777,4 +874,7 @@ Unlike route request handlers, catchers take exactly zero or one parameter. If
the catcher takes a parameter, it must be of type [`&Request`] The [error
catcher example](@example/errors) on GitHub illustrates their use in full.
[`&Request]: @api/rocket/struct.Request.html
[`catch`]: @api/rocket_codegen/attr.catch.html
[`register()`]: @api/rocket/struct.Rocket.html#method.register
[`catchers!`]: @api/rocket_codegen/macro.catchers.html
[`&Request`]: @api/rocket/struct.Request.html

View File

@ -58,6 +58,8 @@ fn json() -> content::Json<&'static str> {
}
```
! warning: This is _not_ the same as the [`Json`] in [`rocket_contrib`]!
[`Accepted`]: @api/rocket/response/status/struct.Accepted.html
[`content::Json`]: @api/rocket/response/content/struct.Json.html
@ -82,12 +84,54 @@ type. For instance, to forward to the catcher for **406 - Not Acceptable**, you
would write:
```rust
use rocket::response::Failure;
#[get("/")]
fn just_fail() -> Failure {
Failure(Status::NotAcceptable)
}
```
## Custom Responders
The [`Responder`] trait documentation details how to implement your own custom
responders by explicitly implementing the trait. For most use cases, however,
Rocket makes it possible to automatically derive an implementation of
`Responder`. In particular, if your custom responder wraps an existing
responder, headers, or sets a custom status or content-type, `Responder` can be
automatically derived:
```rust
#[derive(Responder)]
#[response(status = 500, content_type = "json")]
struct MyResponder {
inner: OtherResponder,
header: SomeHeader,
more: YetAnotherHeader,
#[response(ignore)]
unrelated: MyType,
}
```
For the example above, Rocket generates a `Responder` implementation that:
* Set the response's status to `500: Internal Server Error`.
* Sets the Content-Type to `application/json`.
* Adds the headers `self.header` and `self.more` to the response.
* Completes the response using `self.inner`.
Note that the _first_ field is used as the inner responder while all remaining
fields (unless ignored with `#[response(ignore)]`) are added as headers to the
response. The optional `#[responder]` attribute can be used to customize the
status and content-type of the response. Because `ContentType` and `Status` are
themselves headers, you can also dynamically set the content-type and status by
simply including fields of these types.
For more on using the `Responder` derive, see the [`Responder` derive]
documentation.
[`Responder` derive]: @api/rocket_codegen/derive.Responder.html
## Implementations
Rocket implements `Responder` for many types in Rust's standard library
@ -170,7 +214,8 @@ returned to the client.
## Rocket Responders
Some of Rocket's best features are implemented through responders. You can find
many of these responders in the [`response`] module. Among these are:
many of these responders in the [`response`] module and [`rocket_contrib`]
library. Among these are:
* [`Content`] - Used to override the Content-Type of a response.
* [`NamedFile`] - Streams a file to the client; automatically sets the
@ -179,6 +224,9 @@ many of these responders in the [`response`] module. Among these are:
* [`Stream`] - Streams a response to a client from an arbitrary `Read`er type.
* [`status`] - Contains types that override the status code of a response.
* [`Flash`] - Sets a "flash" cookie that is removed when accessed.
* [`Json`] - Automatically serializes values into JSON.
* [`MsgPack`] - Automatically serializes values into MessagePack.
* [`Template`] - Renders a dynamic template using handlebars or Tera.
[`status`]: @api/rocket/response/status/
[`response`]: @api/rocket/response/
@ -187,6 +235,7 @@ many of these responders in the [`response`] module. Among these are:
[`Redirect`]: @api/rocket/response/struct.Redirect.html
[`Stream`]: @api/rocket/response/struct.Stream.html
[`Flash`]: @api/rocket/response/struct.Flash.html
[`MsgPack`]: @api/rocket_contrib/msgpack/struct.MsgPack.html
### Streaming
@ -287,3 +336,74 @@ fully composed application that makes use of Handlebars templates.
[`Template`]: @api/rocket_contrib/templates/struct.Template.html
[configurable]: ../configuration/#extras
## Typed URIs
Rocket's [`uri!`] macro allows you to build URIs routes in your application in a
robust, type-safe, and URI-safe manner. Type or route parameter mismatches are
caught at compile-time.
The `uri!` returns an [`Origin`] structure with the URI of the supplied route
interpolated with the given values. Note that `Origin` implements `Into<Uri>`
(and by extension, `TryInto<Uri>`), so it can be converted into a [`Uri`] using
`.into()` as needed and passed into methods such as [`Redirect::to()`].
For example, given the following route:
```rust
#[get("/person/<name>/<age>")]
fn person(name: String, age: u8) -> String {
format!("Hello {}! You're {} years old.", name, age)
}
```
URIs to `person` can be created as follows:
```rust
// with unnamed parameters, in route path declaration order
let mike = uri!(person: "Mike Smith", 28);
assert_eq!(mike.path(), "/person/Mike%20Smith/28");
// with named parameters, order irrelevant
let mike = uri!(person: name = "Mike", age = 28);
let mike = uri!(person: age = 28, name = "Mike");
assert_eq!(mike.path(), "/person/Mike/28");
// with a specific mount-point
let mike = uri!("/api", person: name = "Mike", age = 28);
assert_eq!(mike.path(), "/api/person/Mike/28");
```
Rocket informs you of any mismatched parameters at compile-time:
```rust
error: person route uri expects 2 parameter but 1 was supplied
--> src/main.rs:21:19
|
21 | uri!(person: "Mike Smith");
| ^^^^^^^^^^^^
|
= note: expected parameter: age: u8
```
Rocket also informs you of any type errors at compile-time:
```rust
error: the trait bound u8: rocket::http::uri::FromUriParam<&str> is not satisfied
--> src/main:25:23
|
25 | uri!(person: age = "ten", name = "Mike");
| ^^^^^ FromUriParam<&str> is not implemented for u8
|
= note: required by rocket::http::uri::FromUriParam::from_uri_param
```
We recommend that you use `uri!` exclusively when constructing URIs to your
routes.
See the [`uri!`] documentation for more usage details.
[`Origin`]: @api/rocket/http/uri/struct.Origin.html
[`Uri`]: @api/rocket/http/uri/enum.Uri.html
[`Redirect::to()`]: @api/rocket/response/struct.Redirect.html#method.to
[`uri!`]: @api/rocket_codegen/macro.uri.html

View File

@ -19,6 +19,14 @@ The process for using managed state is simple:
2. Add a `State<T>` type to any request handler, where `T` is the type of the
value passed into `manage`.
! note: All managed state must be thread-safe.
Because Rocket automatically multithreads your application, handlers can to
concurrently access managed state. As a result, managed state must be
thread-safe. Thanks to Rust, this condition is checked at compile-time by
ensuring that the type of values you store in managed state implement `Send` +
`Sync`.
### Adding State
To instruct Rocket to manage state for your application, call the
@ -28,6 +36,8 @@ structure with an internal `AtomicUsize` with an initial value of `0`, we can
write the following:
```rust
use std::sync::atomic::AtomicUsize;
struct HitCount {
count: AtomicUsize
}
@ -55,6 +65,8 @@ the managed state. For example, we can retrieve and respond with the current
`HitCount` in a `count` route as follows:
```rust
use rocket::State;
#[get("/count")]
fn count(hit_count: State<HitCount>) -> String {
let current_count = hit_count.count.load(Ordering::Relaxed);
@ -69,9 +81,11 @@ You can retrieve more than one `State` type in a single route as well:
fn state(hit_count: State<HitCount>, config: State<Config>) -> T { ... }
```
If you request a `State<T>` for a `T` that is not `managed`, Rocket won't call
the offending route. Instead, Rocket will log an error message and return a
**500** error to the client.
! warning
If you request a `State<T>` for a `T` that is not `managed`, Rocket won't call
the offending route. Instead, Rocket will log an error message and return a
**500** error to the client.
You can find a complete example using the `HitCount` structure in the [state
example on GitHub](@example/state) and learn more about the [`manage`
@ -94,7 +108,7 @@ fn from_request(req: &'a Request<'r>) -> request::Outcome<T, ()> {
[`Request::guard()`]: @api/rocket/struct.Request.html#method.guard
### Request-Local State
## Request-Local State
While managed state is *global* and available application-wide, request-local
state is *local* to a given request, carried along with the request, and dropped
@ -132,8 +146,8 @@ impl<'a, 'r> FromRequest<'a, 'r> for RequestId {
Note that, without request-local state, it would not be possible to:
1. Associate a piece of data, here an ID, directly with a request.
2. Ensure that a value is generated at most once per request.
1. Associate a piece of data, here an ID, directly with a request.
2. Ensure that a value is generated at most once per request.
For more examples, see the [`FromRequest` request-local state] documentation,
which uses request-local state to cache expensive authentication and

View File

@ -9,9 +9,9 @@ about incoming requests and outgoing responses.
Any type that implements the [`Fairing`] trait is a _fairing_. Fairings hook
into Rocket's request lifecycle, receiving callbacks for events such as incoming
requests and outgoing responses. Rocket passes information about these events to
the fairing, and the fairing can do what it wants with the information. This
includes rewriting data when applicable, recording information about the event
or data, or doing nothing at all.
the fairing; the fairing can do what it wants with the information. This
includes rewriting requests or responses, recording information about the event,
or doing nothing at all.
Rockets fairings are a lot like middleware from other frameworks, but they bear
a few key distinctions:
@ -26,12 +26,15 @@ reaching for fairings instinctively. Before doing so, remember that Rocket
provides a rich set of mechanisms such as [request guards] and [data guards]
that can be used to solve problems in a clean, composable, and robust manner.
As a general rule of thumb, only _globally applicable_ actions should be
effected through fairings. You should _not_ use a fairing to implement
authentication or authorization (preferring to use a [request guard] instead)
_unless_ the authentication or authorization applies to all or most of the
application. On the other hand, you _should_ use a fairing to record timing and
usage statistics or to enforce global security policies.
! warning
As a general rule of thumb, only _globally applicable_ actions should be
effected through fairings. You should **_not_** use a fairing to implement
authentication or authorization (preferring to use a [request guard] instead)
_unless_ the authentication or authorization applies to all or the
overwhelming majority application. On the other hand, you _should_ use a
fairing to record timing and usage statistics or to enforce global security
policies.
[`Fairing`]: @api/rocket/fairing/trait.Fairing.html
[request guard]: ../requests/#request-guards

View File

@ -194,14 +194,20 @@ ROCKET_CODEGEN_DEBUG=1 cargo build
During compilation, you should see output like:
```rust
Emitting item:
fn rocket_route_fn_hello<'_b>(
__req: &'_b ::rocket::Request,
__data: ::rocket::Data
) -> ::rocket::handler::Outcome<'_b> {
let responder = hello();
::rocket::handler::Outcome::from(__req, responder)
}
note: emitting Rocket code generation debug output
--> examples/hello_world/src/main.rs:7:1
|
7 | #[get("/")]
| ^^^^^^^^^^^
|
= note:
fn rocket_route_fn_hello<'_b>(
__req: &'_b ::rocket::Request,
__data: ::rocket::Data
) -> ::rocket::handler::Outcome<'_b> {
let responder = hello();
::rocket::handler::Outcome::from(__req, responder)
}
```
This corresponds to the facade request handler Rocket has generated for the

View File

@ -35,12 +35,13 @@ $ sudo ROCKET_ENV=staging cargo run
=> address: 0.0.0.0
=> port: 8000
=> log: normal
=> workers: [logical cores * 2]
=> workers: 24
=> secret key: generated
=> limits: forms = 32KiB
=> keep-alive: 5s
=> tls: disabled
🛰 Mounting '/':
=> GET /
=> GET / (hello)
🚀 Rocket has launched from http://0.0.0.0:8000
```
@ -64,6 +65,7 @@ value:
address = "localhost"
port = 8000
workers = [number of cpus * 2]
keep_alive = 5
log = "normal"
secret_key = [randomly generated at launch]
limits = { forms = 32768 }
@ -72,6 +74,7 @@ limits = { forms = 32768 }
address = "0.0.0.0"
port = 8000
workers = [number of cpus * 2]
keep_alive = 5
log = "normal"
secret_key = [randomly generated at launch]
limits = { forms = 32768 }
@ -80,6 +83,7 @@ limits = { forms = 32768 }
address = "0.0.0.0"
port = 8000
workers = [number of cpus * 2]
keep_alive = 5
log = "critical"
secret_key = [randomly generated at launch]
limits = { forms = 32768 }
@ -262,6 +266,9 @@ results in `Rocket.toml` and environment variables being ignored.
## Configuring TLS
! warning: Rocket's built-in TLS is **not** considered ready for production use.
It is intended for development use _only_.
Rocket includes built-in, native support for TLS >= 1.2 (Transport Layer
Security). In order for TLS support to be enabled, Rocket must be compiled with
the `"tls"` feature. To do this, add the `"tls"` feature to the `rocket`

View File

@ -2,11 +2,11 @@
Welcome to Rocket!
This is the official guide. It is designed to serve as a starting point to
writing web applications with Rocket and Rust. The guide is also designed to be
a reference for experienced Rocket developers. This guide is conversational in
tone. For concise and purely technical documentation, see the [API
documentation](@api).
This is the official guide for Rocket v0.4. It is designed to serve as a
starting point to writing web applications with Rocket and Rust. The guide is
also designed to be a reference for experienced Rocket developers. This guide is
conversational in tone. For purely technical documentation with examples, see
the [API documentation](@api).
The guide is split into several sections, each with a focus on a different
aspect of Rocket. The sections are:
@ -43,4 +43,3 @@ web IRC client](https://kiwiirc.com/client/irc.mozilla.org/#rocket). You can
learn more about IRC via Mozilla's [Getting Started with
IRC](https://developer.mozilla.org/en-US/docs/Mozilla/QA/Getting_Started_with_IRC)
guide.

View File

@ -80,7 +80,7 @@ code = '''
#[post("/", data = "<task>")]
fn new(task: Form<Task>) -> Flash<Redirect> {
if task.get().description.is_empty() {
if task.description.is_empty() {
Flash::error(Redirect::to("/"), "Cannot be empty.")
} else {
Flash::success(Redirect::to("/"), "Task added.")
@ -106,9 +106,9 @@ code = '''
}
#[put("/<id>", data = "<message>")]
fn update(id: ID, message: Json<Message>) -> JsonValue {
if DB.contains_key(&id) {
DB.insert(id, &message.contents);
fn update(db: &Db, id: Id, message: Json<Message>) -> JsonValue {
if db.contains_key(&id) {
db.insert(id, &message.contents);
json!({ "status": "ok" })
} else {
json!({ "status": "error" })
@ -132,7 +132,7 @@ text = '''
[[bottom_features]]
title = 'Templating'
text = "Rocket makes rendering templates a breeze with built-in templating support."
text = "Rocket makes templating a breeze with built-in templating support."
image = 'templating-icon'
url = 'guide/responses/#templates'
button = 'Learn More'
@ -165,28 +165,36 @@ button = 'Learn More'
color = 'yellow'
margin = -3
[[bottom_features]]
title = 'Query Strings'
text = "Handling query strings and parameters is type-safe and easy in Rocket."
image = 'query-icon'
url = 'guide/requests/#query-strings'
button = 'Learn More'
color = 'orange'
margin = -3
[[bottom_features]]
title = 'Testing Library'
text = "Unit test your applications with ease using the built-in testing library."
image = 'testing-icon'
url = 'guide/testing#testing'
button = 'Learn More'
color = 'green'
color = 'orange'
[[bottom_features]]
title = 'Typed URIs'
text = "Rocket typechecks route URIs for you so you never mistype a URI again."
image = 'ship-icon'
url = 'guide/responses/#typed-uris'
button = 'Learn More'
color = 'green'
margin = -20
# Blocked on Hyper/OpenSSL.
# [[bottom_features]]
# title = 'Signed Sessions'
# text = "Safe, secure, signed sessions are built-in to Rocket so your users can stay safe."
# image = 'sessions-icon'
# url = '/overview'
# title = 'Query Strings'
# text = "Handling query strings and parameters is type-safe and easy in Rocket."
# image = 'query-icon'
# url = 'guide/requests/#query-strings'
# button = 'Learn More'
# color = 'green'
# color = 'red'
# margin = -3
# [[bottom_features]]
# title = 'Private Cookies'
# text = "Safe, secure, private cookies are built-in so your users can stay safe."
# image = 'sessions-icon'
# url = 'guide/requests/#private-cookies'
# button = 'Learn More'
# color = 'purple'

View File

@ -63,7 +63,7 @@ the parameter in the request handler:
```rust
#[post("/login", data = "<user_form>")]
fn login(user_form: Form<UserLogin>) -> String {
// Use `user_form`, return a String.
format!("Hello, {}!", user_form.name)
}
```
@ -143,7 +143,7 @@ look like:
```rust
rocket::ignite()
.mount("/base", routes![index, another])
.launch()
.launch();
```
The `mount` call takes a base path and a set of routes via the `routes!` macro.
@ -182,11 +182,11 @@ fn new_user(admin: AdminUser, new_user: Form<User>) -> T {
For the `new_user` handler above to be called, the following conditions must
hold:
* The request method must be `POST`.
* The request path must be `/user`.
* The request must contain `data` in its body.
* The request metadata must authenticate an `AdminUser`.
* The request body must be a form that parses into a `User` struct.
* The request method must be `POST`.
* The request path must be `/user`.
* The request must contain `data` in its body.
* The request metadata must authenticate an `AdminUser`.
* The request body must be a form that parses into a `User` struct.
'''
[[steps]]
@ -217,13 +217,13 @@ If the function above is used as a handler, for instance, then the type `T` must
implement `Responder`. Rocket provides many useful responder types out of the
box. They include:
* `Json<T>`: Serializes the structure T into JSON and returns it to
the client.
* `Template`: Renders a template file and returns it to the client.
* `Redirect`: Returns a properly formatted HTTP redirect.
* `NamedFile`: Streams a given file to the client with the
Content-Type taken from the files extension.
* `Stream`: Streams data to the client from an arbitrary `Read` value.
* Many Primitive Types: `String`, `&str`, `File`, `Option`, `Result`, and
others all implement the `Responder` trait.
* `Json<T>`: Serializes the structure T into JSON and returns it to
the client.
* `Template`: Renders a template file and returns it to the client.
* `Redirect`: Returns a properly formatted HTTP redirect.
* `NamedFile`: Streams a given file to the client with the
Content-Type taken from the files extension.
* `Stream`: Streams data to the client from an arbitrary `Read` value.
* Many Primitive Types: `String`, `&str`, `File`, `Option`, `Result`, and
others all implement the `Responder` trait.
'''