mirror of https://github.com/rwf2/Rocket.git
Various guide updates for 0.3.
This commit is contained in:
parent
ed14f59c44
commit
1c866f34fa
|
@ -3,8 +3,8 @@
|
|||
Welcome to Rocket!
|
||||
|
||||
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
|
||||
reference for experienced Rocket developers. This guide is conversational in
|
||||
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](https://api.rocket.rs).
|
||||
|
||||
|
@ -21,7 +21,8 @@ aspect of Rocket. The sections are:
|
|||
parsing, and validating.
|
||||
- **[Responses](responses/):** discusses generating responses.
|
||||
- **[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
|
||||
application.
|
||||
- **[Configuration](configuration/):** how to configure a Rocket application.
|
||||
|
|
|
@ -63,7 +63,7 @@ value:
|
|||
[development]
|
||||
address = "localhost"
|
||||
port = 8000
|
||||
workers = [number_of_cpus * 2]
|
||||
workers = [number of cpus * 2]
|
||||
log = "normal"
|
||||
secret_key = [randomly generated at launch]
|
||||
limits = { forms = 32768 }
|
||||
|
@ -71,7 +71,7 @@ limits = { forms = 32768 }
|
|||
[staging]
|
||||
address = "0.0.0.0"
|
||||
port = 80
|
||||
workers = [number_of_cpus * 2]
|
||||
workers = [number of cpus * 2]
|
||||
log = "normal"
|
||||
secret_key = [randomly generated at launch]
|
||||
limits = { forms = 32768 }
|
||||
|
@ -79,7 +79,7 @@ limits = { forms = 32768 }
|
|||
[production]
|
||||
address = "0.0.0.0"
|
||||
port = 80
|
||||
workers = [number_of_cpus * 2]
|
||||
workers = [number of cpus * 2]
|
||||
log = "critical"
|
||||
secret_key = [randomly generated at launch]
|
||||
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
|
||||
`"1.2.3.4"` in every environment:
|
||||
|
||||
```toml
|
||||
```
|
||||
[global]
|
||||
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
|
||||
can be set in `Rocket.toml` as follows:
|
||||
|
||||
```toml
|
||||
```
|
||||
[development]
|
||||
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:
|
||||
|
||||
```toml
|
||||
```
|
||||
[global.tls]
|
||||
certs = "/path/to/certs.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:
|
||||
|
||||
```toml
|
||||
```
|
||||
[development]
|
||||
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
|
||||
`Cargo.toml` file:
|
||||
|
||||
```toml
|
||||
```
|
||||
[dependencies]
|
||||
rocket = { version = "0.2.8", features = ["tls"] }
|
||||
```
|
||||
|
|
|
@ -28,7 +28,7 @@ Rocket project by running the following command in the directory:
|
|||
rustup override set nightly
|
||||
```
|
||||
|
||||
### Minimum Nightly Version
|
||||
### Minimum Nightly
|
||||
|
||||
Rocket always requires the _latest_ version of Rust nightly. If your Rocket
|
||||
application suddently stops building, ensure you're using the latest version of
|
||||
|
@ -86,7 +86,7 @@ run`. You should see the following:
|
|||
=> address: localhost
|
||||
=> port: 8000
|
||||
=> log: normal
|
||||
=> workers: [logical core count * 2]
|
||||
=> workers: [core count * 2]
|
||||
=> secret key: generated
|
||||
=> limits: forms = 32KiB
|
||||
=> tls: disabled
|
||||
|
|
|
@ -40,7 +40,8 @@ lifecycle as the following sequence of steps:
|
|||
|
||||
The remainder of this section details the _routing_ phase as well as additional
|
||||
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
|
||||
|
||||
|
@ -77,12 +78,14 @@ constructing routes.
|
|||
|
||||
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 which are themselves created with the
|
||||
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, and
|
||||
**2)** a list of route handlers through the `routes!` macro. The `routes!` macro
|
||||
ties Rocket's code generation to your application.
|
||||
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:
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
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
|
||||
install or configure such a version. If you don't have Rust installed and want
|
||||
extra guidance installing it, the [getting started](/guide/getting-started)
|
||||
section provides a guide.
|
||||
install or configure such a version. If you don't have Rust installed and would
|
||||
like extra guidance doing so, see the [getting started](/guide/getting-started)
|
||||
section.
|
||||
|
||||
## Running Examples
|
||||
|
||||
|
|
|
@ -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
|
||||
validations. Rocket's code generation takes care of actually validating the
|
||||
proprerties. The remainder of this section describes how to ask Rocket to
|
||||
validate against all of these properties and more.
|
||||
properties. This section describes how to ask Rocket to validate against all of
|
||||
these properties and more.
|
||||
|
||||
## Methods
|
||||
|
||||
|
@ -38,7 +38,9 @@ to the root path:
|
|||
```
|
||||
|
||||
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
|
||||
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
|
||||
your application handles.
|
||||
|
||||
### Reinterpreting Methods
|
||||
### Reinterpreting
|
||||
|
||||
Because browsers only send `GET` and `POST` requests, Rocket _reinterprets_
|
||||
requests under certain conditions. If a `POST` request contains a body of
|
||||
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**
|
||||
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.
|
||||
|
@ -60,7 +62,7 @@ makes use of this feature to submit `PUT` and `DELETE` requests from a web form.
|
|||
## Dynamic Segments
|
||||
|
||||
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:
|
||||
|
||||
```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!`.
|
||||
|
||||
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`].
|
||||
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:
|
||||
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:
|
||||
|
||||
```rust
|
||||
#[get("/hello/<name>/<age>/<cool>")]
|
||||
|
@ -124,11 +126,11 @@ example:
|
|||
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,
|
||||
Rocket _forwards_ the request to the next matching route, if there is any. This
|
||||
continues until a route doesn't forward the request or there are no remaining
|
||||
routes to try. When there are no remaining routes, a customizable **404 error**
|
||||
is returned.
|
||||
What if `cool` isn't a `bool`? Or, what if `age` isn't a `u8`? When a parameter
|
||||
type mismatch occurs, Rocket _forwards_ the request to the next matching route,
|
||||
if there is any. This continues until a route doesn't forward the request or
|
||||
there are no remaining routes to try. When there are no remaining routes, a
|
||||
customizable **404 error** is returned.
|
||||
|
||||
Routes are attempted in increasing _rank_ order. Rocket chooses a default
|
||||
ranking from -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`.
|
||||
|
||||
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
|
||||
_collide_, or can match against similar incoming requests. The `rank` parameter
|
||||
resolves this collision.
|
||||
`user_int` routes, Rocket would emit an error and abort launch, indicating that
|
||||
the routes _collide_, or can match against similar incoming requests. The `rank`
|
||||
parameter resolves this collision.
|
||||
|
||||
### Default Ranking
|
||||
|
||||
|
@ -181,14 +183,14 @@ 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> |
|
||||
| yes | yes | -4 | `/hello?world=true` |
|
||||
| yes | no | -3 | `/hello` |
|
||||
| no | yes | -2 | `/<hi>?world=true` |
|
||||
| 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
|
||||
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.
|
||||
|
@ -235,8 +237,8 @@ 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. (The `data` parameter
|
||||
is described in the next section.)
|
||||
requests with `Content-Type: application/json` will match `new_user`. (The
|
||||
`data` parameter is described in the next section.)
|
||||
|
||||
When a route indicates a non-payload-supporting method (`GET`, `HEAD`, and
|
||||
`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
|
||||
requests with `application/json` as the preferred media type in the `Accept`
|
||||
header will match.
|
||||
header will match `user`.
|
||||
|
||||
## 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
|
||||
named in the route attribute. This is why `param` is not a request guard.
|
||||
|
||||
```rust,ignore
|
||||
```rust
|
||||
#[get("/<param>")]
|
||||
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
|
||||
[`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
|
||||
|
||||
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 { ... }
|
||||
```
|
||||
|
||||
You might also implement `FromRequest` for an `AdminUser` type that validates
|
||||
that the cookies in the incoming request authenticate an administrator. Then,
|
||||
any handler with an `AdminUser` or `ApiKey` type in its argument list is assured
|
||||
to only be invoked if the appropriate conditions are met. Request guards
|
||||
centralize policies, resulting in a simpler, safer, and more secure
|
||||
applications.
|
||||
You might also implement `FromRequest` for an `AdminUser` type that
|
||||
authenticates an administrator using incoming cookies. Then, any handler with an
|
||||
`AdminUser` or `ApiKey` type in its argument list is assured to only be invoked
|
||||
if the appropriate conditions are met. Request guards centralize policies,
|
||||
resulting in a simpler, safer, and more secure applications.
|
||||
|
||||
### Forwarding Guards
|
||||
|
||||
|
@ -353,7 +329,8 @@ We start with two request guards:
|
|||
If no user can be authenticated, the guard forwards.
|
||||
|
||||
We now use these two guards in combination with forwarding to implement the
|
||||
following three routes to some administrative panel at `/admin`:
|
||||
following three routes, each leading to an administrative control panel at
|
||||
`/admin`:
|
||||
|
||||
```rust
|
||||
#[get("/admin")]
|
||||
|
@ -362,7 +339,7 @@ fn admin_panel(admin: AdminUser) -> &'static str {
|
|||
}
|
||||
|
||||
#[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."
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
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,
|
||||
the `admin_panel_redirect` route is tried. Since this route has no guards, it
|
||||
always succeeds. The user is redirected to a log in page.
|
||||
the `admin_panel_redirect` route is attempted. Since this route has no guards,
|
||||
it always succeeds. The user is redirected to a log in page.
|
||||
|
||||
## Cookies
|
||||
|
||||
[`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
|
||||
|
||||
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](https://api.rocket.rs/rocket/data/trait.FromData.html) trait. It
|
||||
in the handler. The argument's type must implement the [`FromData`] trait. It
|
||||
looks like this, where `T: FromData`:
|
||||
|
||||
```rust
|
||||
|
@ -395,6 +488,8 @@ looks like this, where `T: FromData`:
|
|||
fn new(input: T) -> String { ... }
|
||||
```
|
||||
|
||||
[`FromData`]: https://api.rocket.rs/rocket/data/trait.FromData.html
|
||||
|
||||
### Forms
|
||||
|
||||
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
|
||||
implements the
|
||||
[FromForm](https://api.rocket.rs/rocket/request/trait.FromForm.html) 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](https://api.rocket.rs/rocket/request/trait.FromFormValue.html).
|
||||
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:
|
||||
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 { ... }
|
||||
```
|
||||
|
||||
[`FromForm`]: https://api.rocket.rs/rocket/request/trait.FromForm.html
|
||||
[`FromFormValue`]: https://api.rocket.rs/rocket/request/trait.FromFormValue.html
|
||||
|
||||
#### Lenient Parsing
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
a field in a form structure, and implement `FromFormValue` so that it only
|
||||
validates integers over that age:
|
||||
|
@ -554,10 +649,10 @@ The only condition is that the generic type in `JSON` implements the
|
|||
|
||||
### Streaming
|
||||
|
||||
Sometimes you just want to handle the incoming data directly. For example, you
|
||||
might want to stream the incoming data out to a file. Rocket makes this as
|
||||
simple as possible via the
|
||||
[Data](https://api.rocket.rs/rocket/data/struct.Data.html) type:
|
||||
Sometimes you just want to handle incoming data directly. For example, you might
|
||||
want to stream the incoming data out to a file. Rocket makes this as simple as
|
||||
possible via the [`Data`](https://api.rocket.rs/rocket/data/struct.Data.html)
|
||||
type:
|
||||
|
||||
```rust
|
||||
#[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
|
||||
`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
|
||||
text response if the upload succeeds. If the upload fails, an error response is
|
||||
`Content-Type: text/plain` The incoming data is streamed out to
|
||||
`tmp/upload.txt`, and the number of bytes written is returned as a plain text
|
||||
response if the upload succeeds. If the upload fails, an error response is
|
||||
returned. The handler above is complete. It really is that simple! See the
|
||||
[GitHub example
|
||||
code](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.
|
||||
* 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.
|
||||
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
|
||||
|
@ -660,10 +755,9 @@ rocket::ignite().catch(errors![not_found])
|
|||
```
|
||||
|
||||
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
|
||||
[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
|
||||
[error catcher
|
||||
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`
|
||||
type is not particularly useful, and so it is often omitted. The [error catcher
|
||||
example](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/errors) on
|
||||
GitHub illustrates their use in full.
|
||||
|
||||
|
|
|
@ -41,8 +41,7 @@ use rocket::response::status;
|
|||
|
||||
#[post("/<id>")]
|
||||
fn new(id: usize) -> status::Accepted<String> {
|
||||
let url = "http://example.com/resource.json";
|
||||
status::Created(url.into(), Some(format!("id: '{}'", id)))
|
||||
status::Accepted(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
|
||||
including `String`, `&str`, `File`, `Option`, and `Result`. The [`Responder`]
|
||||
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 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`
|
||||
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
|
||||
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
|
||||
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
|
||||
[Stream](https://api.rocket.rs/rocket/response/struct.Stream.html) type, making
|
||||
consuming large amounts of memory. Rocket provides the [`Stream`] type, making
|
||||
this easy. The `Stream` type can be created from any `Read` type. For example,
|
||||
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.
|
||||
|
||||
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
|
||||
the template fairing, simply call `.attach(Template::fairing())` on an instance
|
||||
of `Rocket` as follows:
|
||||
to the instance of Rocket. The [Fairings](/guide/fairings) sections of the guide
|
||||
provides more information on fairings. To attach the template fairing, simply
|
||||
call `.attach(Template::fairing())` on an instance of `Rocket` as follows:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
|
|
|
@ -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 { ... }
|
||||
```
|
||||
|
||||
### Within a Request Guard
|
||||
### Within Guards
|
||||
|
||||
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
|
||||
|
@ -124,7 +124,7 @@ warning: HitCount is not currently being managed by Rocket
|
|||
--> src/main.rs:2:17
|
||||
|
|
||||
2 | fn count(hit_count: State<HitCount>) -> String {
|
||||
| ^^^^^^^^^
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: this State request guard will always fail
|
||||
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
|
||||
example on
|
||||
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
|
||||
[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
|
||||
|
||||
|
@ -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
|
||||
use the following dependencies:
|
||||
|
||||
```toml
|
||||
```
|
||||
[dependencies]
|
||||
rocket = "0.2.8"
|
||||
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
|
||||
code is fairly straightforward: the `DATABASE_URL` environment variable is
|
||||
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`.
|
||||
|
||||
```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
|
||||
single connection from the managed connection pool. We create a new type,
|
||||
|
|
|
@ -14,27 +14,27 @@ instance. Usage is straightforward:
|
|||
|
||||
1. Construct a `Rocket` instance that represents the application.
|
||||
|
||||
```rust
|
||||
let rocket = rocket::ignite();
|
||||
```
|
||||
```rust
|
||||
let rocket = rocket::ignite();
|
||||
```
|
||||
|
||||
2. Construct a `Client` using the `Rocket` instance.
|
||||
|
||||
```rust
|
||||
let client = Client::new(rocket).expect("valid rocket instance");
|
||||
```
|
||||
```rust
|
||||
let client = Client::new(rocket).expect("valid rocket instance");
|
||||
```
|
||||
|
||||
3. Construct requests using the `Client` instance.
|
||||
|
||||
```rust
|
||||
let req = client.get("/");
|
||||
```
|
||||
```rust
|
||||
let req = client.get("/");
|
||||
```
|
||||
|
||||
3. Dispatch the request to retrieve the response.
|
||||
4. Dispatch the request to retrieve the response.
|
||||
|
||||
```rust
|
||||
let response = req.dispatch();
|
||||
```
|
||||
```rust
|
||||
let response = req.dispatch();
|
||||
```
|
||||
|
||||
[`local`]: https://api.rocket.rs/rocket/local/index.html
|
||||
[`Client`]: https://api.rocket.rs/rocket/local/struct.Client.html
|
||||
|
|
|
@ -141,9 +141,9 @@ color = 'blue'
|
|||
|
||||
[[bottom_features]]
|
||||
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'
|
||||
url = '/guide/requests/#request-guards'
|
||||
url = '/guide/requests/#cookies'
|
||||
button = 'Learn More'
|
||||
color = 'purple'
|
||||
margin = -6
|
||||
|
@ -161,14 +161,14 @@ margin = -29
|
|||
title = 'Config Environments'
|
||||
text = "Configure your application your way for development, staging, and production."
|
||||
image = 'config-icon'
|
||||
url = '/guide/overview/#configuration'
|
||||
url = '/guide/configuration/#environment'
|
||||
button = 'Learn More'
|
||||
color = 'yellow'
|
||||
margin = -3
|
||||
|
||||
[[bottom_features]]
|
||||
title = 'Query Params'
|
||||
text = "Handling query parameters isn’t an afterthought in Rocket."
|
||||
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'
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
[[articles]]
|
||||
title = "Rocket v0.2: Managed State & More"
|
||||
author = "Sergio Benitez"
|
||||
author_url = "https://sergio.bz"
|
||||
date = "February 06, 2017"
|
||||
snippet = """
|
||||
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
|
||||
via the [GitHub issue tracker](https://github.com/SergioBenitez/Rocket/issues)
|
||||
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
|
||||
contributors at the end of this article.
|
||||
"""
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
# 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
|
||||
ago. Rocket v0.2 packs a ton of new features, fixes, and general improvements.
|
||||
|
|
Loading…
Reference in New Issue