Various guide updates for 0.3.

This commit is contained in:
Sergio Benitez 2017-07-10 04:59:55 -07:00
parent ed14f59c44
commit 1c866f34fa
12 changed files with 253 additions and 151 deletions

View File

@ -3,8 +3,8 @@
Welcome to Rocket! Welcome to Rocket!
This is the official guide. It is designed to serve as a starting point to This is the official guide. It is designed to serve as a starting point to
writing web application with Rocket and Rust. The guide is also designed to be a writing web applications with Rocket and Rust. The guide is also designed to be
reference for experienced Rocket developers. This guide is conversational in a reference for experienced Rocket developers. This guide is conversational in
tone. For concise and purely technical documentation, see the [API tone. For concise and purely technical documentation, see the [API
documentation](https://api.rocket.rs). documentation](https://api.rocket.rs).
@ -21,7 +21,8 @@ aspect of Rocket. The sections are:
parsing, and validating. parsing, and validating.
- **[Responses](responses/):** discusses generating responses. - **[Responses](responses/):** discusses generating responses.
- **[State](state/):** how to manage state in a Rocket application. - **[State](state/):** how to manage state in a Rocket application.
- **[Fairings](fairings/):** introduction to Rocket's structure middleware. - **[Fairings](fairings/):** provides an overview of Rocket's structured
middleware.
- **[Testing](testing/):** how to unit and integration test a Rocket - **[Testing](testing/):** how to unit and integration test a Rocket
application. application.
- **[Configuration](configuration/):** how to configure a Rocket application. - **[Configuration](configuration/):** how to configure a Rocket application.

View File

@ -63,7 +63,7 @@ value:
[development] [development]
address = "localhost" address = "localhost"
port = 8000 port = 8000
workers = [number_of_cpus * 2] workers = [number of cpus * 2]
log = "normal" log = "normal"
secret_key = [randomly generated at launch] secret_key = [randomly generated at launch]
limits = { forms = 32768 } limits = { forms = 32768 }
@ -71,7 +71,7 @@ limits = { forms = 32768 }
[staging] [staging]
address = "0.0.0.0" address = "0.0.0.0"
port = 80 port = 80
workers = [number_of_cpus * 2] workers = [number of cpus * 2]
log = "normal" log = "normal"
secret_key = [randomly generated at launch] secret_key = [randomly generated at launch]
limits = { forms = 32768 } limits = { forms = 32768 }
@ -79,7 +79,7 @@ limits = { forms = 32768 }
[production] [production]
address = "0.0.0.0" address = "0.0.0.0"
port = 80 port = 80
workers = [number_of_cpus * 2] workers = [number of cpus * 2]
log = "critical" log = "critical"
secret_key = [randomly generated at launch] secret_key = [randomly generated at launch]
limits = { forms = 32768 } limits = { forms = 32768 }
@ -98,7 +98,7 @@ overrides if already present, that parameter in every environment. For example,
given the following `Rocket.toml` file, the value of `address` will be given the following `Rocket.toml` file, the value of `address` will be
`"1.2.3.4"` in every environment: `"1.2.3.4"` in every environment:
```toml ```
[global] [global]
address = "1.2.3.4" address = "1.2.3.4"
@ -119,7 +119,7 @@ application, can use them as they wish. As an example, the
accepts a value for the `template_dir` configuration parameter. The parameter accepts a value for the `template_dir` configuration parameter. The parameter
can be set in `Rocket.toml` as follows: can be set in `Rocket.toml` as follows:
```toml ```
[development] [development]
template_dir = "dev_templates/" template_dir = "dev_templates/"
@ -179,7 +179,7 @@ parameter. The value of `tls` must be a table with two keys:
The recommended way to specify these parameters is via the `global` environment: The recommended way to specify these parameters is via the `global` environment:
```toml ```
[global.tls] [global.tls]
certs = "/path/to/certs.pem" certs = "/path/to/certs.pem"
key = "/path/to/key.pem" key = "/path/to/key.pem"
@ -187,7 +187,7 @@ key = "/path/to/key.pem"
Of course, you can always specify the configuration values per environment: Of course, you can always specify the configuration values per environment:
```toml ```
[development] [development]
tls = { certs = "/path/to/certs.pem", key = "/path/to/key.pem" } tls = { certs = "/path/to/certs.pem", key = "/path/to/key.pem" }
``` ```
@ -196,7 +196,7 @@ 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` dependency in your feature. To do this, add the `"tls"` feature to the `rocket` dependency in your
`Cargo.toml` file: `Cargo.toml` file:
```toml ```
[dependencies] [dependencies]
rocket = { version = "0.2.8", features = ["tls"] } rocket = { version = "0.2.8", features = ["tls"] }
``` ```

View File

@ -28,7 +28,7 @@ Rocket project by running the following command in the directory:
rustup override set nightly rustup override set nightly
``` ```
### Minimum Nightly Version ### Minimum Nightly
Rocket always requires the _latest_ version of Rust nightly. If your Rocket Rocket always requires the _latest_ version of Rust nightly. If your Rocket
application suddently stops building, ensure you're using the latest version of application suddently stops building, ensure you're using the latest version of
@ -86,7 +86,7 @@ run`. You should see the following:
=> address: localhost => address: localhost
=> port: 8000 => port: 8000
=> log: normal => log: normal
=> workers: [logical core count * 2] => workers: [core count * 2]
=> secret key: generated => secret key: generated
=> limits: forms = 32KiB => limits: forms = 32KiB
=> tls: disabled => tls: disabled

View File

@ -40,7 +40,8 @@ lifecycle as the following sequence of steps:
The remainder of this section details the _routing_ phase as well as additional The remainder of this section details the _routing_ phase as well as additional
components needed for Rocket to begin dispatching requests to request handlers. components needed for Rocket to begin dispatching requests to request handlers.
The sections following describe the request and response phases. The sections following describe the request and response phases as well as other
components of Rocket.
## Routing ## Routing
@ -77,12 +78,14 @@ constructing routes.
Before Rocket can dispatch requests to a route, the route needs to be _mounted_. 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` Mounting a route is like namespacing it. Routes are mounted via the `mount`
method on a `Rocket` instance which are themselves created with the method on a `Rocket` instance. A `Rocket` instance is typically created with the
`rocket::ignite()` static method. `rocket::ignite()` static method.
The `mount` method takes **1)** a path to namespace a list of routes under, and The `mount` method takes:
**2)** a list of route handlers through the `routes!` macro. The `routes!` macro
ties Rocket's code generation to your application. 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 For instance, to mount the `world` route we declared above, we can write the
following: following:

View File

@ -2,9 +2,9 @@
Before you can start writing a Rocket application, you'll need a **nightly** Before you can start writing a Rocket application, you'll need a **nightly**
version of Rust installed. We recommend you use [rustup](https://rustup.rs/) to version of Rust installed. We recommend you use [rustup](https://rustup.rs/) to
install or configure such a version. If you don't have Rust installed and want install or configure such a version. If you don't have Rust installed and would
extra guidance installing it, the [getting started](/guide/getting-started) like extra guidance doing so, see the [getting started](/guide/getting-started)
section provides a guide. section.
## Running Examples ## Running Examples

View File

@ -23,8 +23,8 @@ Among other things, you can ask Rocket to automatically validate:
The route attribute and function signature work in tandem to describe these The route attribute and function signature work in tandem to describe these
validations. Rocket's code generation takes care of actually validating the validations. Rocket's code generation takes care of actually validating the
proprerties. The remainder of this section describes how to ask Rocket to properties. This section describes how to ask Rocket to validate against all of
validate against all of these properties and more. these properties and more.
## Methods ## Methods
@ -38,7 +38,9 @@ to the root path:
``` ```
The grammar for these attributes is defined formally in the The grammar for these attributes is defined formally in the
[rocket_codegen](https://api.rocket.rs/rocket_codegen/) API docs. [`rocket_codegen`](https://api.rocket.rs/rocket_codegen/) API docs.
### HEAD Requests
Rocket handles `HEAD` requests automatically when there exists a `GET` route Rocket handles `HEAD` requests automatically when there exists a `GET` route
that would otherwise match. It does this by stripping the body from the that would otherwise match. It does this by stripping the body from the
@ -46,10 +48,10 @@ 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 request by declaring a route for it; Rocket won't interfere with `HEAD` requests
your application handles. your application handles.
### Reinterpreting Methods ### Reinterpreting
Because browsers only send `GET` and `POST` requests, Rocket _reinterprets_ Because browsers can only send `GET` and `POST` requests, Rocket _reinterprets_
requests under certain conditions. If a `POST` request contains a body of 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 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. `"PUT"`), that field's value is used as the method for the incoming request.
@ -60,7 +62,7 @@ makes use of this feature to submit `PUT` and `DELETE` requests from a web form.
## Dynamic Segments ## Dynamic Segments
You can declare path segments as dynamic by using angle brackets around variable You can declare path segments as dynamic by using angle brackets around variable
names in a route's path. For example, if we wanted to say _Hello!_ to anything, 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: not just the world, we can declare a route like so:
```rust ```rust
@ -76,11 +78,11 @@ any request to a path with two non-empty segments, where the first segment is
visit `/hello/John`, the application would respond with `Hello, John!`. 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 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`]. type, including your own, as long as the type implements the [`FromParam`]
Rocket implements `FromParam` for many of the standard library types, as well as trait. Rocket implements `FromParam` for many of the standard library types, as
a few special Rocket types. For the full list of supplied implementations, see well as a few special Rocket types. For the full list of supplied
the [`FromParam` API docs]. Here's a more complete route to illustrate varied implementations, see the [`FromParam` API docs]. Here's a more complete route to
usage: illustrate varied usage:
```rust ```rust
#[get("/hello/<name>/<age>/<cool>")] #[get("/hello/<name>/<age>/<cool>")]
@ -124,11 +126,11 @@ example:
fn hello(name: String, age: u8, cool: bool) -> String { ... } fn hello(name: String, age: u8, cool: bool) -> String { ... }
``` ```
What if `cool` isn't a `bool`? Or, what if `age` isn't a `u8`? In this case, What if `cool` isn't a `bool`? Or, what if `age` isn't a `u8`? When a parameter
Rocket _forwards_ the request to the next matching route, if there is any. This type mismatch occurs, Rocket _forwards_ the request to the next matching route,
continues until a route doesn't forward the request or there are no remaining if there is any. This continues until a route doesn't forward the request or
routes to try. When there are no remaining routes, a customizable **404 error** there are no remaining routes to try. When there are no remaining routes, a
is returned. customizable **404 error** is returned.
Routes are attempted in increasing _rank_ order. Rocket chooses a default 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 ranking from -4 to -1, detailed in the next section, for all routes, but a
@ -167,9 +169,9 @@ would never forward. An `Ok` variant would indicate that `<id>` was a valid
`Err`'s value would contain the string that failed to parse as a `usize`. `Err`'s value would contain the string that failed to parse as a `usize`.
By the way, if you were to omit the `rank` parameter in the `user_str` or By the way, if you were to omit the `rank` parameter in the `user_str` or
`user_int` routes, Rocket would emit a warning indicating that the routes `user_int` routes, Rocket would emit an error and abort launch, indicating that
_collide_, or can match against similar incoming requests. The `rank` parameter the routes _collide_, or can match against similar incoming requests. The `rank`
resolves this collision. parameter resolves this collision.
### Default Ranking ### Default Ranking
@ -181,14 +183,14 @@ of a route given its properties.
| static path | query string | rank | example | | static path | query string | rank | example |
| ------------- | -------------- | ------ | ------------------- | | ------------- | -------------- | ------ | ------------------- |
| yes | yes | -4 | /hello?world=true | | yes | yes | -4 | `/hello?world=true` |
| yes | no | -3 | /hello | | yes | no | -3 | `/hello` |
| no | yes | -2 | /&lt;hi>?world=true | | no | yes | -2 | `/<hi>?world=true` |
| no | no | -1 | /&lt;hi> | | no | no | -1 | `/<hi>` |
## Many Dynamic Segments ## Multiple Segments
You can also match against multiple segments by using `<param..>` in the route You can also match against multiple segments by using `<param..>` in a route
path. The type of such parameters, known as _segments_ parameters, must path. The type of such parameters, known as _segments_ parameters, must
implement [`FromSegments`]. Segments parameters must be the final component of a 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. path: any text after a segments parameter will result in a compile-time error.
@ -235,8 +237,8 @@ fn new_user(user: JSON<User>) -> T { ... }
``` ```
The `format` parameter in the `post` attribute declares that only incoming The `format` parameter in the `post` attribute declares that only incoming
requests with `Content-Type: application/json` will match. (The `data` parameter requests with `Content-Type: application/json` will match `new_user`. (The
is described in the next section.) `data` parameter is described in the next section.)
When a route indicates a non-payload-supporting method (`GET`, `HEAD`, and When a route indicates a non-payload-supporting method (`GET`, `HEAD`, and
`OPTIONS`), the `format` route parameter instructs Rocket to check against the `OPTIONS`), the `format` route parameter instructs Rocket to check against the
@ -253,7 +255,7 @@ fn user(id: usize) -> JSON<User> { ... }
The `format` parameter in the `get` attribute declares that only incoming The `format` parameter in the `get` attribute declares that only incoming
requests with `application/json` as the preferred media type in the `Accept` requests with `application/json` as the preferred media type in the `Accept`
header will match. header will match `user`.
## Request Guards ## Request Guards
@ -274,7 +276,7 @@ As an example, 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 `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. This is why `param` is not a request guard.
```rust,ignore ```rust
#[get("/<param>")] #[get("/<param>")]
fn index(param: isize, a: A, b: B, c: C) -> ... { ... } fn index(param: isize, a: A, b: B, c: C) -> ... { ... }
``` ```
@ -288,31 +290,6 @@ documentation.
[`FromRequest`]: https://api.rocket.rs/rocket/request/trait.FromRequest.html [`FromRequest`]: https://api.rocket.rs/rocket/request/trait.FromRequest.html
[`Cookies`]: https://api.rocket.rs/rocket/http/enum.Cookies.html [`Cookies`]: https://api.rocket.rs/rocket/http/enum.Cookies.html
### Retrieving Metadata
Sometimes we need data associated with a request that isn't a direct data input.
Rocket makes retrieving and validating such information easy through request
guards. As an example, consider the built-in [`Cookies`] request guard. Because
`Cookies` is a request guard, an argument of its type can simply be added to a
handler:
```rust
use rocket::http::Cookies;
#[get("/")]
fn index(cookies: Cookies) -> Option<String> {
cookies.get("message")
.map(|value| format!("Message: {}", value))
}
```
This results in the incoming request's cookies being accessible from the
handler. The [cookies example] on GitHub illustrates further use of the
`Cookies` type to get and set cookies, while the [`Cookies`] documentation
contains full usage information.
[cookies example]: https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/cookies
### Custom Guards ### Custom Guards
You can implement `FromRequest` for your own types. For instance, to protect a You can implement `FromRequest` for your own types. For instance, to protect a
@ -325,12 +302,11 @@ then use it as a request guard:
fn sensitive(key: ApiKey) -> &'static str { ... } fn sensitive(key: ApiKey) -> &'static str { ... }
``` ```
You might also implement `FromRequest` for an `AdminUser` type that validates You might also implement `FromRequest` for an `AdminUser` type that
that the cookies in the incoming request authenticate an administrator. Then, authenticates an administrator using incoming cookies. Then, any handler with an
any handler with an `AdminUser` or `ApiKey` type in its argument list is assured `AdminUser` or `ApiKey` type in its argument list is assured to only be invoked
to only be invoked if the appropriate conditions are met. Request guards if the appropriate conditions are met. Request guards centralize policies,
centralize policies, resulting in a simpler, safer, and more secure resulting in a simpler, safer, and more secure applications.
applications.
### Forwarding Guards ### Forwarding Guards
@ -353,7 +329,8 @@ We start with two request guards:
If no user can be authenticated, the guard forwards. If no user can be authenticated, the guard forwards.
We now use these two guards in combination with forwarding to implement the We now use these two guards in combination with forwarding to implement the
following three routes to some administrative panel at `/admin`: following three routes, each leading to an administrative control panel at
`/admin`:
```rust ```rust
#[get("/admin")] #[get("/admin")]
@ -362,7 +339,7 @@ fn admin_panel(admin: AdminUser) -> &'static str {
} }
#[get("/admin", rank = 2)] #[get("/admin", rank = 2)]
fn admin_panel_user(admin: User) -> &'static str { fn admin_panel_user(user: User) -> &'static str {
"Sorry, you must be an administrator to access this page." "Sorry, you must be an administrator to access this page."
} }
@ -376,18 +353,134 @@ The three routes above encode authentication _and_ authorization. The
`admin_panel` route only succeeds if an administrator is logged in. Only then is `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` route the admin panel displayed. If the user is not an admin, the `AdminUser` route
will forward. Since the `admin_panel_user` route is ranked next highest, it is will forward. Since the `admin_panel_user` route is ranked next highest, it is
tried next. This route succeeds if there is _any_ user signed in, and an 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, authorization failure message is displayed. Finally, if a user isn't signed in,
the `admin_panel_redirect` route is tried. Since this route has no guards, it the `admin_panel_redirect` route is attempted. Since this route has no guards,
always succeeds. The user is redirected to a log in page. it always succeeds. The user is redirected to a log in page.
## Cookies
[`Cookies`] is an important, built-in request guard: it allows you to get, set,
and remove cookies. Because `Cookies` is a request guard, an argument of its
type can simply be added to a handler:
```rust
use rocket::http::Cookies;
#[get("/")]
fn index(cookies: Cookies) -> Option<String> {
cookies.get("message")
.map(|value| format!("Message: {}", 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 `Cookies` guard. The [cookies example] on GitHub
illustrates further use of the `Cookies` type to get and set cookies, while the
[`Cookies`] documentation contains complete usage information.
[cookies example]: https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/cookies
### Private Cookies
Cookies added via the [`Cookies::add()`] method are set _in the clear._ In other
words, the value set is visible by the client. For sensitive data, Rocket
provides _private_ cookies.
Private cookies are just like regular cookies except that they are encrypted
using authenticated encryption, a form of encryption which simultaneously
provides confidentiality, integrity, and authenticity. This means that 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.
The API for retrieving, adding, and removing private cookies is identical except
methods are suffixed with `_private`. These methods are: [`get_private`],
[`add_private`], and [`remove_private`]. An example of their usage is below:
```rust
/// Retrieve the user's ID, if any.
#[get("/user_id")]
fn user_id(cookies: Cookies) -> Option<String> {
request.cookies()
.get_private("user_id")
.map(|cookie| format!("User ID: {}", cookie.value()))
}
/// Remove the `user_id` cookie.
#[post("/logout")]
fn logout(mut cookies: Cookies) -> Flash<Redirect> {
cookies.remove_private(Cookie::named("user_id"));
Flash::success(Redirect::to("/login"), "Successfully logged out.")
}
```
[`Cookies::add()`]: https://api.rocket.rs/rocket/http/enum.Cookies.html#method.add
### Secret Key
To encrypt private cookies, Rocket uses the 256-bit key specified in the
`secret_key` configuration parameter. If one is not specified, Rocket will
automatically generate a fresh key. Note, however, that a private cookie can
only be decrypted with the same key with which it was encrypted. As such, it is
important to set a `secret_key` configuration parameter when using private
cookies so that cookies decrypt properly after an application restart. Rocket
emits a warning if an application is run in production without a configured
`secret_key`.
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](/guide/configuration) section of the guide.
[`get_private`]: https://api.rocket.rs/rocket/http/enum.Cookies.html#method.get_private
[`add_private`]: https://api.rocket.rs/rocket/http/enum.Cookies.html#method.add_private
[`remove_private`]: https://api.rocket.rs/rocket/http/enum.Cookies.html#method.remove_private
### One-At-A-Time
For safety reasons, Rocket currently requires that at most one `Cookies`
instance be active at a time. It's uncommon to run into this restriction, but it
can be confusing to handle if it does crop up.
If this does happen, Rocket will emit messages to the console that look as
follows:
```
=> Error: Multiple `Cookies` instances are active at once.
=> An instance of `Cookies` must be dropped before another can be retrieved.
=> Warning: The retrieved `Cookies` instance will be empty.
```
The messages will be emitted when a violating handler is called. The issue can
be resolved by ensuring that two instances of `Cookies` cannot be active at once
due to the offending handler. A common error is to have a handler that uses a
`Cookies` request guard as well as a `Custom` request guard that retrieves
`Cookies`, as so:
```rust
#[get("/")]
fn bad(cookies: Cookies, custom: Custom) { .. }
```
Because the `cookies` guard will fire before the `custom` guard, the `custom`
guard will retrieve an instance of `Cookies` when one already exists for
`cookies`. This scenario can be fixed by simply swapping the order of the
guards:
```rust
#[get("/")]
fn good(custom: Custom, cookies: Cookies) { .. }
```
## Body Data ## Body Data
At some point, your web application will need to process body data. 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 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 expects data, annotate it with `data = "<param>"`, where `param` is an argument
in the handler. The argument's type must implement the in the handler. The argument's type must implement the [`FromData`] trait. It
[FromData](https://api.rocket.rs/rocket/data/trait.FromData.html) trait. It
looks like this, where `T: FromData`: looks like this, where `T: FromData`:
```rust ```rust
@ -395,6 +488,8 @@ looks like this, where `T: FromData`:
fn new(input: T) -> String { ... } fn new(input: T) -> String { ... }
``` ```
[`FromData`]: https://api.rocket.rs/rocket/data/trait.FromData.html
### Forms ### Forms
Forms are the most common type of data handled in web applications, and Rocket Forms are the most common type of data handled in web applications, and Rocket
@ -415,23 +510,23 @@ fn new(task: Form<Task>) -> String { ... }
``` ```
The `Form` type implements the `FromData` trait as long as its generic parameter The `Form` type implements the `FromData` trait as long as its generic parameter
implements the implements the [`FromForm`] trait. In the example, we've derived the `FromForm`
[FromForm](https://api.rocket.rs/rocket/request/trait.FromForm.html) trait. In trait automatically for the `Task` structure. `FromForm` can be derived for any
the example, we've derived the `FromForm` trait automatically for the `Task` structure whose fields implement [`FromFormValue`]. If a `POST /todo` request
structure. `FromForm` can be derived for any structure whose fields implement arrives, the form data will automatically be parsed into the `Task` structure.
[FromFormValue](https://api.rocket.rs/rocket/request/trait.FromFormValue.html). If the data that arrives isn't of the correct Content-Type, the request is
If a `POST /todo` request arrives, the form data will automatically be parsed forwarded. If the data doesn't parse or is simply invalid, a customizable `400 -
into the `Task` structure. If the data that arrives isn't of the correct Bad Request` or `422 - Unprocessable Entity` error is returned. As before, a
Content-Type, the request is forwarded. If the data doesn't parse or is simply forward or failure can be caught by using the `Option` and `Result` types:
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 ```rust
#[post("/todo", data = "<task>")] #[post("/todo", data = "<task>")]
fn new(task: Option<Form<Task>>) -> String { ... } fn new(task: Option<Form<Task>>) -> String { ... }
``` ```
[`FromForm`]: https://api.rocket.rs/rocket/request/trait.FromForm.html
[`FromFormValue`]: https://api.rocket.rs/rocket/request/trait.FromFormValue.html
#### Lenient Parsing #### Lenient Parsing
Rocket's `FromForm` parsing is _strict_ by default. In other words, A `Form<T>` Rocket's `FromForm` parsing is _strict_ by default. In other words, A `Form<T>`
@ -489,7 +584,7 @@ Rocket will then match the form field named `type` to the structure field named
#### Field Validation #### Field Validation
Fields of forms can be easily validated via implementations of the Fields of forms can be easily validated via implementations of the
`FromFormValue` trait. For example, if you'd like to verify that some user is [`FromFormValue`] trait. For example, if you'd like to verify that some user is
over some age in a form, then you might define a new `AdultAge` type, use it as over some age in a form, then you might define a new `AdultAge` type, use it as
a field in a form structure, and implement `FromFormValue` so that it only a field in a form structure, and implement `FromFormValue` so that it only
validates integers over that age: validates integers over that age:
@ -554,10 +649,10 @@ The only condition is that the generic type in `JSON` implements the
### Streaming ### Streaming
Sometimes you just want to handle the incoming data directly. For example, you Sometimes you just want to handle incoming data directly. For example, you might
might want to stream the incoming data out to a file. Rocket makes this as want to stream the incoming data out to a file. Rocket makes this as simple as
simple as possible via the possible via the [`Data`](https://api.rocket.rs/rocket/data/struct.Data.html)
[Data](https://api.rocket.rs/rocket/data/struct.Data.html) type: type:
```rust ```rust
#[post("/upload", format = "text/plain", data = "<data>")] #[post("/upload", format = "text/plain", data = "<data>")]
@ -567,9 +662,9 @@ fn upload(data: Data) -> io::Result<String> {
``` ```
The route above accepts any `POST` request to the `/upload` path with The route above accepts any `POST` request to the `/upload` path with
`Content-Type` `text/plain` The incoming data is streamed out to `Content-Type: text/plain` The incoming data is streamed out to
`tmp/upload.txt` file, and the number of bytes written is returned as a plain `tmp/upload.txt`, and the number of bytes written is returned as a plain text
text response if the upload succeeds. If the upload fails, an error response is 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 returned. The handler above is complete. It really is that simple! See the
[GitHub example [GitHub example
code](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/raw_upload) code](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/raw_upload)
@ -638,7 +733,7 @@ Routing may fail for a variety of reasons. These include:
* A handler returns a [`Responder`](/guide/responses/#responder) that fails. * A handler returns a [`Responder`](/guide/responses/#responder) that fails.
* No matching route was found. * No matching route was found.
If any of these conditions occurs, Rocket returns an error to the client. To do If any of these conditions occur, Rocket returns an error to the client. To do
so, Rocket invokes the _error catcher_ corresponding to the error's status code. so, Rocket invokes the _error catcher_ corresponding to the error's status code.
A catcher is like a route, except it only handles errors. Catchers are declared A catcher is like a route, except it only handles errors. Catchers are declared
via the `error` attribute, which takes a single integer corresponding to the via the `error` attribute, which takes a single integer corresponding to the
@ -660,10 +755,9 @@ rocket::ignite().catch(errors![not_found])
``` ```
Unlike request handlers, error handlers can only take 0, 1, or 2 parameters of Unlike request handlers, error handlers can only take 0, 1, or 2 parameters of
types [Request](https://api.rocket.rs/rocket/struct.Request.html) and/or types [`Request`](https://api.rocket.rs/rocket/struct.Request.html) and/or
[Error](https://api.rocket.rs/rocket/enum.Error.html). At present, the `Error` [`Error`](https://api.rocket.rs/rocket/enum.Error.html). At present, the `Error`
type is not particularly useful, and so it is often omitted. The type is not particularly useful, and so it is often omitted. The [error catcher
[error catcher
example](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/errors) on example](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/errors) on
GitHub illustrates their use in full. GitHub illustrates their use in full.

View File

@ -41,8 +41,7 @@ use rocket::response::status;
#[post("/<id>")] #[post("/<id>")]
fn new(id: usize) -> status::Accepted<String> { fn new(id: usize) -> status::Accepted<String> {
let url = "http://example.com/resource.json"; status::Accepted(Some(format!("id: '{}'", id)))
status::Created(url.into(), Some(format!("id: '{}'", id)))
} }
``` ```
@ -90,13 +89,13 @@ fn just_fail() -> Failure {
} }
``` ```
## `std` Implementations ## Implementations
Rocket implements `Responder` for many types in Rust's standard library Rocket implements `Responder` for many types in Rust's standard library
including `String`, `&str`, `File`, `Option`, and `Result`. The [`Responder`] including `String`, `&str`, `File`, `Option`, and `Result`. The [`Responder`]
documentation describes these in detail, but we briefly cover a few here. documentation describes these in detail, but we briefly cover a few here.
### `&str` and `String` ### Strings
The `Responder` implementations for `&str` and `String` are straight-forward: The `Responder` implementations for `&str` and `String` are straight-forward:
the string is used as a sized body, and the Content-Type of the response is set the string is used as a sized body, and the Content-Type of the response is set
@ -124,7 +123,7 @@ fn handler() -> &'static str {
} }
``` ```
### `Option<T>` **where** `T: Responder` ### `Option`
`Option` is _wrapping_ responder: an `Option<T>` can only be returned when `T` `Option` is _wrapping_ responder: an `Option<T>` can only be returned when `T`
implements `Responder`. If the `Option` is `Some`, the wrapped responder is used implements `Responder`. If the `Option` is `Some`, the wrapped responder is used
@ -143,7 +142,7 @@ fn files(file: PathBuf) -> Option<NamedFile> {
} }
``` ```
### `Result<T, E>` **where** `E: Debug`, `E: Responder` ### `Result`
`Result` is a special kind of wrapping responder: its functionality depends on `Result` is a special kind of wrapping responder: its functionality depends on
whether the error type `E` implements `Responder`. whether the error type `E` implements `Responder`.
@ -194,8 +193,7 @@ many of these responders in the [`response`] module. Among these are:
The `Stream` type deserves special attention. When a large amount of data needs The `Stream` type deserves special attention. When a large amount of data needs
to be sent to the client, it is better to stream the data to the client to avoid to be sent to the client, it is better to stream the data to the client to avoid
consuming large amounts of memory. Rocket provides the consuming large amounts of memory. Rocket provides the [`Stream`] type, making
[Stream](https://api.rocket.rs/rocket/response/struct.Stream.html) type, making
this easy. The `Stream` type can be created from any `Read` type. For example, this easy. The `Stream` type can be created from any `Read` type. For example,
to stream from a local Unix stream, we might write: to stream from a local Unix stream, we might write:
@ -266,9 +264,9 @@ example, if a file ends with `.hbs`, Handlebars is used, while if a file ends
with `.tera`, Tera is used. with `.tera`, Tera is used.
For templates to be properly registered, the template fairing must be attached For templates to be properly registered, the template fairing must be attached
to the instance of Rocket. Fairings are explained in the next section. To attach to the instance of Rocket. The [Fairings](/guide/fairings) sections of the guide
the template fairing, simply call `.attach(Template::fairing())` on an instance provides more information on fairings. To attach the template fairing, simply
of `Rocket` as follows: call `.attach(Template::fairing())` on an instance of `Rocket` as follows:
```rust ```rust
fn main() { fn main() {

View File

@ -69,7 +69,7 @@ You can retrieve more than one `State` type in a single route as well:
fn state(hit_count: State<HitCount>, config: State<Config>) -> T { ... } fn state(hit_count: State<HitCount>, config: State<Config>) -> T { ... }
``` ```
### Within a Request Guard ### Within Guards
It can also be useful to retrieve managed state from a `FromRequest` It can also be useful to retrieve managed state from a `FromRequest`
implementation. To do so, simple invoke `State<T>` as a guard using the implementation. To do so, simple invoke `State<T>` as a guard using the
@ -124,7 +124,7 @@ warning: HitCount is not currently being managed by Rocket
--> src/main.rs:2:17 --> src/main.rs:2:17
| |
2 | fn count(hit_count: State<HitCount>) -> String { 2 | fn count(hit_count: State<HitCount>) -> String {
| ^^^^^^^^^ | ^^^^^^^^
| |
= note: this State request guard will always fail = note: this State request guard will always fail
help: maybe add a call to 'manage' here? help: maybe add a call to 'manage' here?
@ -143,9 +143,9 @@ globally, add `#![allow(unmanaged_state)]` to your crate attributes.
You can find a complete example using the `HitCount` structure in the [state You can find a complete example using the `HitCount` structure in the [state
example on example on
GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/state) and GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/state) and
learn more about the [manage learn more about the [`manage`
method](https://api.rocket.rs/rocket/struct.Rocket.html#method.manage) and method](https://api.rocket.rs/rocket/struct.Rocket.html#method.manage) and
[State type](https://api.rocket.rs/rocket/struct.State.html) in the API docs. [`State` type](https://api.rocket.rs/rocket/struct.State.html) in the API docs.
## Databases ## Databases
@ -175,7 +175,7 @@ detailed information on how to use Diesel, please see the [Diesel getting
started guide](http://diesel.rs/guides/getting-started/). For this example, we started guide](http://diesel.rs/guides/getting-started/). For this example, we
use the following dependencies: use the following dependencies:
```toml ```
[dependencies] [dependencies]
rocket = "0.2.8" rocket = "0.2.8"
diesel = { version = "*", features = ["sqlite"] } diesel = { version = "*", features = ["sqlite"] }
@ -204,7 +204,7 @@ advocates for using a `DATABASE_URL` environment variable to set the database
URL, and we use the same convention here. Excepting the long-winded types, the URL, and we use the same convention here. Excepting the long-winded types, the
code is fairly straightforward: the `DATABASE_URL` environment variable is code is fairly straightforward: the `DATABASE_URL` environment variable is
stored in the `DATABASE_URL` static, and an `r2d2::Pool` is created using the stored in the `DATABASE_URL` static, and an `r2d2::Pool` is created using the
default configuration parameter and a Diesel `SqliteConnection` default configuration parameters and a Diesel `SqliteConnection`
`ConnectionManager`. `ConnectionManager`.
```rust ```rust
@ -235,7 +235,7 @@ fn main() {
} }
``` ```
### Connection Request Guard ### Connection Guard
The second and final step is to implement a request guard that retrieves a The second and final step is to implement a request guard that retrieves a
single connection from the managed connection pool. We create a new type, single connection from the managed connection pool. We create a new type,

View File

@ -30,7 +30,7 @@ instance. Usage is straightforward:
let req = client.get("/"); let req = client.get("/");
``` ```
3. Dispatch the request to retrieve the response. 4. Dispatch the request to retrieve the response.
```rust ```rust
let response = req.dispatch(); let response = req.dispatch();

View File

@ -141,9 +141,9 @@ color = 'blue'
[[bottom_features]] [[bottom_features]]
title = 'Cookies' title = 'Cookies'
text = "Cookies are first-class in Rocket. View, add, or remove cookies without hassle." text = "View, add, or remove cookies, with or without encryption, without hassle."
image = 'cookies-icon' image = 'cookies-icon'
url = '/guide/requests/#request-guards' url = '/guide/requests/#cookies'
button = 'Learn More' button = 'Learn More'
color = 'purple' color = 'purple'
margin = -6 margin = -6
@ -161,14 +161,14 @@ margin = -29
title = 'Config Environments' title = 'Config Environments'
text = "Configure your application your way for development, staging, and production." text = "Configure your application your way for development, staging, and production."
image = 'config-icon' image = 'config-icon'
url = '/guide/overview/#configuration' url = '/guide/configuration/#environment'
button = 'Learn More' button = 'Learn More'
color = 'yellow' color = 'yellow'
margin = -3 margin = -3
[[bottom_features]] [[bottom_features]]
title = 'Query Params' title = 'Query Strings'
text = "Handling query parameters isnt an afterthought in Rocket." text = "Handling query strings and parameters is type-safe and easy in Rocket."
image = 'query-icon' image = 'query-icon'
url = '/guide/requests/#query-strings' url = '/guide/requests/#query-strings'
button = 'Learn More' button = 'Learn More'

View File

@ -1,5 +1,7 @@
[[articles]] [[articles]]
title = "Rocket v0.2: Managed State & More" title = "Rocket v0.2: Managed State & More"
author = "Sergio Benitez"
author_url = "https://sergio.bz"
date = "February 06, 2017" date = "February 06, 2017"
snippet = """ snippet = """
Today marks the first major release since Rocket's debut a little over a month Today marks the first major release since Rocket's debut a little over a month
@ -7,7 +9,7 @@ ago. Rocket v0.2 packs a ton of new features, fixes, and general improvements.
Much of the development in v0.2 was led by the community, either through reports Much of the development in v0.2 was led by the community, either through reports
via the [GitHub issue tracker](https://github.com/SergioBenitez/Rocket/issues) via the [GitHub issue tracker](https://github.com/SergioBenitez/Rocket/issues)
or via direct contributions. In fact, there have been **20 unique contributors** or via direct contributions. In fact, there have been **20 unique contributors**
to Rocket's code since Rocket's initial introduction! Community feedback has to Rocket's codebase since Rocket's initial introduction! Community feedback has
been incredible. As a special thank you, we include the names of these been incredible. As a special thank you, we include the names of these
contributors at the end of this article. contributors at the end of this article.
""" """

View File

@ -1,6 +1,10 @@
# Rocket v0.2: Managed State & More # Rocket v0.2: Managed State & More
**Posted by [Sergio Benitez](https://sergio.bz) on February 06, 2017** <p class="metadata"><strong>
Posted by <a href="https://sergio.bz">Sergio Benitez</a> on February 06, 2017
</strong></p>
<!-- we should be able to generate the title and subheading -->
Today marks the first major release since Rocket's debut a little over a month Today marks the first major release since Rocket's debut a little over a month
ago. Rocket v0.2 packs a ton of new features, fixes, and general improvements. ago. Rocket v0.2 packs a ton of new features, fixes, and general improvements.