mirror of
https://github.com/rwf2/Rocket.git
synced 2025-01-17 23:19:06 +00:00
Update guide in full for current 0.4.0-dev.
This commit is contained in:
parent
9ef0b731c8
commit
3903ffdb47
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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`
|
||||
|
@ -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/).
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
Rocket’s 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
|
||||
|
@ -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
|
||||
|
@ -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`
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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 file’s 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 file’s 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.
|
||||
'''
|
||||
|
Loading…
Reference in New Issue
Block a user