mirror of
https://github.com/rwf2/Rocket.git
synced 2025-01-19 16:09:08 +00:00
Add 0.5 migration guide, FAQ to site docs.
This commit is contained in:
parent
3616f25c0b
commit
a361bbd899
807
site/guide/01-upgrading-from-0.4.md
Normal file
807
site/guide/01-upgrading-from-0.4.md
Normal file
@ -0,0 +1,807 @@
|
|||||||
|
# Upgrading
|
||||||
|
|
||||||
|
Rocket v0.5 bring many new features and improvements over Rocket v0.4. Along
|
||||||
|
with new features, Rocket v0.5 also includes many changes that improve the
|
||||||
|
overall usability, stability, and security of the framework and applications
|
||||||
|
written in it. While the Rust compiler can guide you through many of these
|
||||||
|
changes, others require special attention. The intent of this guide is to guide
|
||||||
|
you through these changes and more, migrating your Rocket application to 0.5 and
|
||||||
|
reaping the benefits of new features and improvements.
|
||||||
|
|
||||||
|
This guide is _not_ intended to replace, but instead complement, a reading of
|
||||||
|
the [CHANGELOG]. The [CHANGELOG] should be considered required reading for all
|
||||||
|
developers wishing to migrate their applications to Rocket v0.5.
|
||||||
|
|
||||||
|
[CHANGELOG]: @github/CHANGELOG.md
|
||||||
|
|
||||||
|
## Crate Organization
|
||||||
|
|
||||||
|
Rocket v0.5 incorporates an improved module structure and crate ecosystem.
|
||||||
|
Modules and items that have been moved or removed will trigger a compiler error.
|
||||||
|
We encourage users to search through the [CHANGELOG] or [API docs](@api/rocket)
|
||||||
|
for the v0.5 analog. All previously existing functionality, except for that
|
||||||
|
incompatible with async I/O, is available in v0.5.
|
||||||
|
|
||||||
|
### Off-by-Default Secrets
|
||||||
|
|
||||||
|
The `private-cookies` crate feature, which was previously enabled by default,
|
||||||
|
has been renamed to `secrets` and is disabled by default. If you are using
|
||||||
|
private cookies, you _must_ enable the `secrets` feature in `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
rocket = { version = "0.5.0-rc.1", features = ["secrets"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Contrib Deprecation
|
||||||
|
|
||||||
|
The `rocket_contrib` crate is deprecated and is wholly incompatible with Rocket
|
||||||
|
0.5. _All_ users of `rocket_contrib` _must_:
|
||||||
|
|
||||||
|
* Remove all dependencies and references to `rocket_contrib`.
|
||||||
|
* For templating support, depend on the new [`rocket_dyn_templates`] crate.
|
||||||
|
* For database pools, depend on the new [`rocket_sync_db_pools`] and/or
|
||||||
|
[`rocket_db_pools`] crates.
|
||||||
|
* Enable [features in `rocket`] as necessary.
|
||||||
|
|
||||||
|
For example, to make use of JSON and Tera templates, make the following changes
|
||||||
|
to `Cargo.toml`:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
[dependencies]
|
||||||
|
- rocket = "0.4"
|
||||||
|
- rocket_contrib = { version = "0.4", features = ["json"], default-features = false }
|
||||||
|
+ rocket = { version = "0.5.0-rc.1", features = ["json"] }
|
||||||
|
+ rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
! note: `rocket_dyn_templates` _does not_ follow in version lock-step with
|
||||||
|
the `rocket` crate.
|
||||||
|
|
||||||
|
This is intentional. The crate depends on many external dependencies which may
|
||||||
|
evolve at a different pace than Rocket itself. Allowing their versions to
|
||||||
|
diverge enables keeping dependencies up-to-date without breaking `rocket`
|
||||||
|
itself.
|
||||||
|
|
||||||
|
All features previously present in `rocket_contrib` are available elsewhere. The
|
||||||
|
[contrib graduation] section of the CHANGELOG contains full details on the
|
||||||
|
changes.
|
||||||
|
|
||||||
|
|
||||||
|
[`rocket_dyn_templates`]: @api/rocket_dyn_templates
|
||||||
|
[`rocket_sync_db_pools`]: @api/rocket_sync_db_pools
|
||||||
|
[`rocket_db_pools`]: @api/rocket_db_pools
|
||||||
|
[features in `rocket`]: @api/rocket/#features
|
||||||
|
[contrib graduation]: @github/CHANGELOG.md#contrib-graduation
|
||||||
|
|
||||||
|
## Stable and Async Support
|
||||||
|
|
||||||
|
Rocket v0.5 compiles and builds on Rust stable with an entirely asynchronous
|
||||||
|
core. You are encouraged to:
|
||||||
|
|
||||||
|
* Switch to the Rust stable release channel for production builds.
|
||||||
|
* Remove the previously required `#![feature(..)]` crate attribute.
|
||||||
|
|
||||||
|
All application authors _must_:
|
||||||
|
|
||||||
|
* Use `rocket::build()` instead of `rocket::ignite()`.
|
||||||
|
* Use either the `#[launch]` or `#[rocket::main]` async entry attribute.
|
||||||
|
* Use `async` versions of any blocking I/O or execute it in another thread.
|
||||||
|
|
||||||
|
Application authors _may_:
|
||||||
|
|
||||||
|
* Prefer to explicitly import macros via `use` instead of `#[macro_use]`.
|
||||||
|
|
||||||
|
The rest of the section describes making these changes in detail.
|
||||||
|
|
||||||
|
### Stable Release Channel
|
||||||
|
|
||||||
|
If you prefer to use Rust's stable release channel, you can switch to it using
|
||||||
|
`rustup`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
## switch globally
|
||||||
|
rustup default nightly
|
||||||
|
|
||||||
|
## switch locally
|
||||||
|
rustup override set stable
|
||||||
|
```
|
||||||
|
|
||||||
|
Using the stable release channel ensures that _no_ breakages will occur when
|
||||||
|
upgrading your Rust compiler or Rocket. That being said, Rocket continues to
|
||||||
|
take advantage of features only present in the nightly channel. As a result, the
|
||||||
|
development experience will continue to be better on nightly for the forseeable
|
||||||
|
future. For example, compiler diagnostics on `nightly` are more detailed and
|
||||||
|
accurate:
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Example Diagnostic on Nightly</summary>
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
error: invalid parameters for `has_two` route uri
|
||||||
|
--> $DIR/typed-uris-bad-params.rs:55:18
|
||||||
|
|
|
||||||
|
55 | uri!(has_two(id = 100, cookies = "hi"));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: uri parameters are: id: i32, name: String
|
||||||
|
= help: missing parameter: `name`
|
||||||
|
help: unknown parameter: `cookies`
|
||||||
|
--> $DIR/typed-uris-bad-params.rs:55:28
|
||||||
|
|
|
||||||
|
55 | uri!(has_two(id = 100, cookies = "hi"));
|
||||||
|
| ^^^^^^^
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Example Diagnostic on Stable</summary>
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
error: invalid parameters for `has_two` route uri
|
||||||
|
--- note: uri parameters are: id: i32, name: String
|
||||||
|
--- help: missing parameter: `name`
|
||||||
|
--> $DIR/typed-uris-bad-params.rs:55:18
|
||||||
|
|
|
||||||
|
55 | uri!(has_two(id = 100, cookies = "hi"));
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: [help] unknown parameter: `cookies`
|
||||||
|
--> $DIR/typed-uris-bad-params.rs:55:28
|
||||||
|
|
|
||||||
|
55 | uri!(has_two(id = 100, cookies = "hi"));
|
||||||
|
| ^^^^^^^
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
Our **recommendation** is to develop locally on the nightly channel but build
|
||||||
|
and deploy for production on the stable channel.
|
||||||
|
|
||||||
|
### Feature Attribute
|
||||||
|
|
||||||
|
As a result support for the stable release channel, Rocket applications no
|
||||||
|
longer need to enable any features to be used. You should **remove any
|
||||||
|
`#[feature(..)]` crate attributes:**
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- #![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
-
|
||||||
|
#[macro_use] extern crate rocket;
|
||||||
|
|
||||||
|
fn main() { .. }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updates to Launch
|
||||||
|
|
||||||
|
The new asynchronous core requires an async runtime to run. The new
|
||||||
|
[`launch`] and [`main`] attributes simplify starting a runtime suitable for
|
||||||
|
running Rocket applications. You should use [`launch`] whenever possible.
|
||||||
|
|
||||||
|
At the same time, the `rocket::ignite()` function has been renamed to
|
||||||
|
[`rocket::build()`]; calls to the function or method should be replaced
|
||||||
|
accordingly. Together, these two changes result in the following diff to what
|
||||||
|
was previously the `main` function:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- fn main() {
|
||||||
|
- rocket::ignite().mount("/hello", routes![hello]).launch();
|
||||||
|
- }
|
||||||
|
+ #[launch]
|
||||||
|
+ fn rocket() -> _ {
|
||||||
|
+ rocket::build().mount("/hello", routes![hello])
|
||||||
|
+ }
|
||||||
|
```
|
||||||
|
|
||||||
|
[`launch`]: @api/rocket/attr.launch.html
|
||||||
|
[`main`]: @api/rocket/attr.main.html
|
||||||
|
[`rocket::build()`]: @api/rocket/struct.Rocket.html#method.build
|
||||||
|
|
||||||
|
### Blocking I/O
|
||||||
|
|
||||||
|
Rocket v0.5 takes advantage of the latest developments in async I/O in Rust by
|
||||||
|
migrating to a fully asynchronous core powered by [`tokio`]. Specifically,
|
||||||
|
_every_ request is handled by an asynchronous task which internally calls one or
|
||||||
|
more request handlers. Asynchronous tasks are multiplexed on a [configurable
|
||||||
|
number of worker threads]. Though there is no limit on the number of tasks that
|
||||||
|
can run concurrently, at most `worker` tasks can be run in parallel.
|
||||||
|
|
||||||
|
The runtime can switch between tasks in a single worker thread _iff_ an `await`
|
||||||
|
point in reached. In other words, context switching is _cooperative_, _not_
|
||||||
|
preemptive. This _iff_ is critical: if an `await` point is _not_ reached, no
|
||||||
|
task switching can occur. As such, it is important that `await` points occur
|
||||||
|
periodically in a task so that tasks waiting to be scheduled are not starved.
|
||||||
|
|
||||||
|
In general, when working with `async` APIs, await points will occur naturally.
|
||||||
|
However, an application written for synchronous I/O, like all Rocket
|
||||||
|
applications prior to 0.4, must take great care to convert all synchronous,
|
||||||
|
blocking I/O, to `async` I/O. This is because, as the name implies, blocking I/O
|
||||||
|
blocks a thread from making progress until the I/O result is available, meaning
|
||||||
|
that no tasks can be scheduled on the waiting thread, wasting valuable resources
|
||||||
|
and significantly degrading performance.
|
||||||
|
|
||||||
|
Common sources of blocking I/O and their `async` replacements include:
|
||||||
|
|
||||||
|
* Anything in `std::fs`: replace with `rocket::tokio::fs`.
|
||||||
|
* Anything in `std::sync`: replace with `rocket::tokio::sync`.
|
||||||
|
* Anything in `std::net`: replace with `rocket::tokio::net`.
|
||||||
|
* Anything in `std::io`: replace with `rocket::tokio::io`.
|
||||||
|
* Sleep or timers: replace with `rocket::tokio::time`.
|
||||||
|
* Any networking: replace with `rocket::tokio::net`.
|
||||||
|
* Any file system access: replace with `rocket::tokio::fs`.
|
||||||
|
|
||||||
|
Unfortunately, the Rust compiler provides no support for identifying blocking
|
||||||
|
I/O via lints or compile-time checks: it is up to you to scan your application
|
||||||
|
for sources of blocking I/O and replace them with their `async` counterpart. If
|
||||||
|
no such counterpart exists, you must execute the I/O in its own thread by using
|
||||||
|
[`rocket::tokio::task::spawn_blocking`].
|
||||||
|
|
||||||
|
All of Rocket's I/O APIs have been updated to be `async`-safe.
|
||||||
|
This results in requiring `.await` calls for common APIs like [`NamedFile`]. To
|
||||||
|
use `.await` in a route, the handler must be marked with `async`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use rocket::get;
|
||||||
|
use rocket::fs::NamedFile;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
async fn index() -> Option<NamedFile> {
|
||||||
|
NamedFile::open("index.html").await.ok()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>See a diff of the changes from v0.4.</summary>
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- use rocket::response::NamedFile;
|
||||||
|
+ use rocket::fs::NamedFile;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
- fn index() -> Option<NamedFile> {
|
||||||
|
- NamedFile::open("index.html").ok()
|
||||||
|
+ async fn index() -> Option<NamedFile> {
|
||||||
|
+ NamedFile::open("index.html").await.ok()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[`tokio`]: https://tokio.rs
|
||||||
|
[configurable number of worker threads]: ../configuration/#workers
|
||||||
|
[`NamedFile`]: @api/rocket/fs/struct.NamedFile.html
|
||||||
|
[`rocket::tokio::task::spawn_blocking`]: @tokio/task/fn.spawn_blocking.html
|
||||||
|
|
||||||
|
### Blocking Compute
|
||||||
|
|
||||||
|
By the same reasoning, performing large amounts of compute (really, just another
|
||||||
|
form of I/O) can prevent other tasks from executing in a timely manner. If you
|
||||||
|
are performing long computations in a handler, you should execute the
|
||||||
|
computation in its own thread, again using [`rocket::tokio::task::spawn_blocking`]:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use rocket::get;
|
||||||
|
use rocket::tokio::task;
|
||||||
|
use rocket::response::Debug;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
async fn exepensive() -> Result<(), Debug<task::JoinError>> {
|
||||||
|
let result = task::spawn_blocking(move || {
|
||||||
|
// perform the computation
|
||||||
|
}).await?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Async Traits
|
||||||
|
|
||||||
|
To support `async` methods in traits, Rocket provides the [`async_trait`]
|
||||||
|
attribute. The attribute _must_ be applied to all implementations of _async
|
||||||
|
traits_ like [`FromRequest`] and [`Fairing`]:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
use rocket::request::{self, Request, FromRequest};
|
||||||
|
|
||||||
|
+ #[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for MyType {
|
||||||
|
type Error = MyError;
|
||||||
|
|
||||||
|
- fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
+ async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
/* .. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All trait documentation has been updated to call out such traits with an example
|
||||||
|
implementation that includes the invocation. The example implementation also
|
||||||
|
serves as better documentation for trait and trait method signatures than the
|
||||||
|
rustdocs. Because `async_trait` modifies these signatures, the rustdocs diverge
|
||||||
|
from what is written in source. For example, rustdoc renders:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
fn from_request<'life0, 'async_trait>(
|
||||||
|
request: &'r Request<'life0>
|
||||||
|
) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>> + Send + 'async_trait>>;
|
||||||
|
```
|
||||||
|
|
||||||
|
...whereas the source looks like:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Unfortunately, rustdoc does not provide a mechanism to render the source as it
|
||||||
|
is written. As such, we encourage all authors to use the examples as the source
|
||||||
|
of truth for trait and method signatures.
|
||||||
|
|
||||||
|
[`async_trait`]: @api/rocket/attr.async_trait.html
|
||||||
|
[`FromRequest`]: @api/rocket/request/trait.FromRequest.html
|
||||||
|
[`Fairing`]: @api/rocket/fairing/trait.Fairing.html
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Rocket's configuration system has been entirely revamped for 0.5. The
|
||||||
|
[configuration](../configuration) section of the guide contains a full
|
||||||
|
walkthrough of the new system while the [general changes] section of the
|
||||||
|
CHANGELOG contains further details on configuration changes. We call out the
|
||||||
|
most important of these changes here. All users _must_:
|
||||||
|
|
||||||
|
* Replace `ROCKET_ENV` environment variable use with `ROCKET_PROFILE`.
|
||||||
|
* Replace `ROCKET_LOG` environment variable with `ROCKET_LOG_LEVEL`.
|
||||||
|
* Use only IP addreses for the `address` configuration parameter.
|
||||||
|
* Replace the `dev` or `development` profile with `debug`.
|
||||||
|
* Note that the `stage`, `staging`, `prod`, and `production` profiles carry no
|
||||||
|
special meaning in v0.5.
|
||||||
|
* Use `0` to disable `keep_alive` instead of `false` or `off`.
|
||||||
|
* Replace uses of "extras" with [typed extraction].
|
||||||
|
|
||||||
|
Rocket will emit warnings at launch time if use of the previous functionality is
|
||||||
|
detected.
|
||||||
|
|
||||||
|
### Profiles
|
||||||
|
|
||||||
|
The new system deals with "profiles" where there were previously "environments".
|
||||||
|
As opposed to environments, profiles:
|
||||||
|
|
||||||
|
* Can be arbitrarily named, and any number can exist.
|
||||||
|
* Match Rust profiles in naming: `debug` and `release` are the default
|
||||||
|
profiles for the respective Rust compilation profile.
|
||||||
|
* Are programmatically selectable and configurable.
|
||||||
|
* Have a `default` profile with fallback values for all profiles.
|
||||||
|
* Have a `global` profile with overrides for all profiles.
|
||||||
|
|
||||||
|
Authors should read the new [configuration](../configuration) section of the
|
||||||
|
guide to determine the scope of changes required. This likely includes:
|
||||||
|
|
||||||
|
* Defining most configuration in the `default` profile instead.
|
||||||
|
* Using the `debug` profile where `dev` or `development` was used.
|
||||||
|
* Using the `release` profile where `prod` or `production` was used.
|
||||||
|
|
||||||
|
[general changes]: @github/CHANGELOG.md#general
|
||||||
|
[typed extraction]: ../configuration/#extracting-values
|
||||||
|
|
||||||
|
### Typed Extraction
|
||||||
|
|
||||||
|
The "extras" configuration in v0.4 is entirely replaced by [typed extraction],
|
||||||
|
which allows any `Deserialize` structure to be derived from configuration
|
||||||
|
sources. All users _should_ make use of typed extraction where "extras" were
|
||||||
|
being used previously. The diff below illustrates one such example:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
use rocket::fairing::AdHoc;
|
||||||
|
|
||||||
|
+ #[derive(Deserialize)]
|
||||||
|
struct AppConfig {
|
||||||
|
id: Option<usize>,
|
||||||
|
port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
- fn main() {
|
||||||
|
- rocket::ignite()
|
||||||
|
- .attach(AdHoc::on_attach("Token Config", |rocket| {
|
||||||
|
- println!("Adding token managed state from config...");
|
||||||
|
- let id = match rocket.config().get_int("id") {
|
||||||
|
- Ok(v) if v >= 0 => Some(v as usize),
|
||||||
|
- _ => None,
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
- let port = match rocket.config().get_int("port") {
|
||||||
|
- Ok(v) if v => 0 && v < 1 << 16 => v as u16,
|
||||||
|
- _ => return Err(rocket)
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
- Ok(rocket.manage(AppConfig { id, port }))
|
||||||
|
- }))
|
||||||
|
- }
|
||||||
|
|
||||||
|
+ #[launch]
|
||||||
|
+ fn rocket() -> _ {
|
||||||
|
+ rocket::build().attach(AdHoc::config::<AppConfig>())
|
||||||
|
+ }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Routing
|
||||||
|
|
||||||
|
Rocket v0.5 brings several major changes that affect routing:
|
||||||
|
|
||||||
|
1. [Default ranking] is more precise, so fewer routes need manual ranking.
|
||||||
|
2. Multi-segment route parameters (`<foo..>`) now match _zero_ or more
|
||||||
|
segments.
|
||||||
|
3. Parameters are _always_ percent-decoded, so `&RawStr` no longer implements
|
||||||
|
`FromParam`.
|
||||||
|
4. Query parameters parse with [`FromForm`] instead of `FromQuery` and support
|
||||||
|
arbitrarily collections, nesting, structures, etc.
|
||||||
|
5. All UTF-8 characters are allowed in static path components: `#[get("/❤️")]`.
|
||||||
|
6. The [`register()`] method require a path to [scope catchers] under. Using
|
||||||
|
`"/"` emulates the previous behavior.
|
||||||
|
|
||||||
|
[Default ranking]: ../requests#default-ranking
|
||||||
|
[`FromForm`]: @api/rocket/form/trait.FromForm.html
|
||||||
|
[`FromParam`]: @api/rocket/request/trait.FromParam.html
|
||||||
|
[`register()`]: @api/rocket/struct.Rocket.html#method.register
|
||||||
|
[scope catchers]: ../requests/#scoping
|
||||||
|
|
||||||
|
### Default Ranks
|
||||||
|
|
||||||
|
Default route ranking now takes into account partially dynamic paths, increasing
|
||||||
|
the range of default ranks from `[-6, -1]` to `[-12, -1]`. The net effect is
|
||||||
|
that fewer routes collide by default, requiring less manual ranking. For
|
||||||
|
example, the following two routes collide in 0.4 but not in 0.5:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use rocket::get;
|
||||||
|
|
||||||
|
#[get("/foo/<_>/bar")]
|
||||||
|
fn foo_bar() { }
|
||||||
|
|
||||||
|
#[get("/<_..>")]
|
||||||
|
fn everything() { }
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>See a diff of the changes from v0.4.</summary>
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- #[get("/foo/<_>/bar", rank = 1)]
|
||||||
|
+ #[get("/foo/<_>/bar")]
|
||||||
|
fn foo_bar() { }
|
||||||
|
|
||||||
|
- #[get("/<_..>", rank = 2)]
|
||||||
|
+ #[get("/<_..>")]
|
||||||
|
fn everything() { }
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
**The recommendation** is to remove all unnecessary manual ranking parameters.
|
||||||
|
For smaller applications, you may find that _all_ manual ranks can be removed.
|
||||||
|
Larger applications may still require ranks to resolve ambiguities.
|
||||||
|
|
||||||
|
### Kleen Multi-Segments
|
||||||
|
|
||||||
|
The multi-segment route parameter `<foo..>` now matches _zero or more_ segments,
|
||||||
|
a change from the previous _one_ or more segments. The implication is two-fold:
|
||||||
|
|
||||||
|
1. Where previously two routes were required to match a prefix and its
|
||||||
|
suffixes, now one suffices:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- #[get("/")]
|
||||||
|
- fn index();
|
||||||
|
|
||||||
|
- #[get("/<path..>")]
|
||||||
|
- fn rest(path: PathBuf);
|
||||||
|
|
||||||
|
+ #[get("/<path..>")]
|
||||||
|
+ fn all(path: PathBuf);
|
||||||
|
```
|
||||||
|
|
||||||
|
2. A prefix collides with a route that matches all of its suffixes. For
|
||||||
|
example, `index` and `rest` above collide.
|
||||||
|
|
||||||
|
Most applications will likely benefit from this change by allowing the extra
|
||||||
|
prefix-only route to be removed entirely. If the previous functionality of
|
||||||
|
requiring at least one segment is desired, a route that explicitly matches the
|
||||||
|
first segment can be used:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use std::path::PathBuf;
|
||||||
|
# use rocket::get;
|
||||||
|
|
||||||
|
#[get("/<first>/<rest..>")]
|
||||||
|
fn rest(first: PathBuf, rest: PathBuf) { /* .. */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fewer Raw Strings
|
||||||
|
|
||||||
|
Rocket v0.5 makes a concerted effort to limit the exposure to strings from the
|
||||||
|
raw HTTP payload. In line with this philosophy, Rocket now percent-decodes all
|
||||||
|
incoming parameters automatically as opposed to doing so on-demand. The
|
||||||
|
corollary is three-fold:
|
||||||
|
|
||||||
|
1. The `&RawStr` type no longer implements [`FromParam`].
|
||||||
|
2. The `&str` type now implements [`FromParam`] and is fully decoded.
|
||||||
|
3. The `String` parameter type is identical to the `&str` type and should be
|
||||||
|
avoided.
|
||||||
|
|
||||||
|
Most applications can simply swap uses of `&RawStr` and `String` for `&str` in
|
||||||
|
routes, forms, and so on to benefit from the increase web-safety and
|
||||||
|
performance. For instance, the front-page example becomes:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
#[get("/<name>/<age>")]
|
||||||
|
- fn hello(name: String, age: u8) -> String {
|
||||||
|
+ fn hello(name: &str, age: u8) -> String {
|
||||||
|
format!("Hello, {} year old named {}!", age, name)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A form that previously used `String` becomes:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
#[derive(FromForm)]
|
||||||
|
- struct MyForm {
|
||||||
|
+ struct MyForm<'r> {
|
||||||
|
- value: String,
|
||||||
|
+ value: &'r str,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Queries as Forms
|
||||||
|
|
||||||
|
Query strings in Rocket v0.5 are in parity with forms and support their [full
|
||||||
|
breadth](../requests#forms). Single segment query parameters (`<foo>`) should
|
||||||
|
require little to no changes, except that they now support collections,
|
||||||
|
structures, and any other `FromForm` type. This implies that the majority, if
|
||||||
|
not _all_ custom `FromQuery` implementations, should be derivable via `FromForm`
|
||||||
|
or have a built-in equivalent like `Vec<T>`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use rocket::post;
|
||||||
|
|
||||||
|
#[post("/?<numbers>")]
|
||||||
|
fn form(numbers: Vec<usize>) { /* .. */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
Multi-segment query parameters (`<foo..>`) no longer require the use of a
|
||||||
|
`Form<T>` guard. Instead, `T` can be used directly:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
#[derive(FromForm)]
|
||||||
|
struct Person { /* .. */ }
|
||||||
|
|
||||||
|
#[get("/hello?<person..>")]
|
||||||
|
- fn hello(person: Option<Form<Person>>)
|
||||||
|
+ fn hello(person: Option<Person>)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Forms
|
||||||
|
|
||||||
|
Rocket v0.5 introduces entirely revamped [forms] with support for:
|
||||||
|
|
||||||
|
* [Multipart uploads.](../requests#multipart)
|
||||||
|
* [Collections: maps, vectors, and more.](../requests#collections)
|
||||||
|
* [Nesting.](../requests#nesting)
|
||||||
|
* [Ad-Hoc validation.](../requests#ad-hoc-validation)
|
||||||
|
|
||||||
|
Additionally, the [`FromForm` derive] has been substantially improved so that
|
||||||
|
nearly all custom implementations of `FromForm` or (the now defunct)
|
||||||
|
`FromFormValue` can be derived. Altogether, this means that any external crate
|
||||||
|
dependency for form handling and most custom `FromForm` or `FromFormValue`
|
||||||
|
implementations are unnecessary and should be removed.
|
||||||
|
|
||||||
|
### Multipart
|
||||||
|
|
||||||
|
If your application used an external crate to accept multipart form submissions,
|
||||||
|
the dependency should be removed: Rocket v0.5 natively handles multipart. A file
|
||||||
|
upload can be accepted via the [`TempFile`] form guard:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# #[macro_use] extern crate rocket;
|
||||||
|
|
||||||
|
use rocket::form::Form;
|
||||||
|
use rocket::fs::TempFile;
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
struct Upload<'r> {
|
||||||
|
save: bool,
|
||||||
|
file: TempFile<'r>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/upload", data = "<upload>")]
|
||||||
|
fn upload(upload: Form<Upload<'_>>) { /* .. */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
[`TempFile`]: @api/rocket/fs/enum.TempFile.html
|
||||||
|
|
||||||
|
### Field Validation
|
||||||
|
|
||||||
|
In Rocket v0.4, it was encouraged and often required to implement
|
||||||
|
`FromFormValue` to introduce typed field validation. In v0.5, this can be
|
||||||
|
accomplished by [deriving `FromForm`]:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- use rocket::request::FromFormValue;
|
||||||
|
- use rocket::http::RawStr;
|
||||||
|
-
|
||||||
|
- struct AdultAge(usize);
|
||||||
|
-
|
||||||
|
- impl<'v> FromFormValue<'v> for AdultAge {
|
||||||
|
- type Error = &'v RawStr;
|
||||||
|
-
|
||||||
|
- fn from_form_value(form_value: &'v RawStr) -> Result<AdultAge, &'v RawStr> {
|
||||||
|
- match form_value.parse::<usize>() {
|
||||||
|
- Ok(age) if age >= 21 => Ok(AdultAge(age)),
|
||||||
|
- _ => Err(form_value),
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
|
||||||
|
+ #[derive(FromForm)]
|
||||||
|
+ #[field(validate = range(21..))]
|
||||||
|
+ struct AdultAge(usize);
|
||||||
|
```
|
||||||
|
|
||||||
|
If a given validation is used once, a new type may offer no additional safety.
|
||||||
|
The validation can be performed directly on a field:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rocket::form::FromForm;
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
struct MyForm {
|
||||||
|
#[field(validate = range(21..))]
|
||||||
|
age: usize,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[forms]: ../requests#forms
|
||||||
|
[`FromForm` derive]: @api/rocket/derive.FromForm.html
|
||||||
|
[deriving `FromForm`]: @api/rocket/derive.FromForm.html
|
||||||
|
|
||||||
|
## Notable New Features
|
||||||
|
|
||||||
|
Rocket v0.5 brings an abundance of new features that enable new functionality,
|
||||||
|
increase productivity, and make existing applications more robust. We encourage
|
||||||
|
all users to take advantage of these new features.
|
||||||
|
|
||||||
|
### Sentinels
|
||||||
|
|
||||||
|
Rocket v0.5 introduces [sentinels]. Entirely unique to Rocket, sentinels offer
|
||||||
|
an automatic last line of defense against runtime errors by enabling any type
|
||||||
|
that appears in a route to abort application launch if invalid conditions are
|
||||||
|
detected. For example, the [`&State<T>`] guard in v0.5 is a [`Sentinel`] that
|
||||||
|
aborts launch if the type `T` is not in managed state, thus preventing
|
||||||
|
associated runtime errors.
|
||||||
|
|
||||||
|
You should consider implementing `Sentinel` for your types if you have guards
|
||||||
|
(request, data, form, etc.) or responders that depend on `Rocket` state to
|
||||||
|
function properly. For example, consider a `MyResponder` that expects:
|
||||||
|
|
||||||
|
* An error catcher to be registered for the `400` status code.
|
||||||
|
* A specific type `T` to be in managed state.
|
||||||
|
|
||||||
|
Making `MyResponder` a sentinel that guards against these conditions is as
|
||||||
|
simple as:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rocket::{Rocket, Ignite, Sentinel};
|
||||||
|
# struct MyResponder;
|
||||||
|
# struct T;
|
||||||
|
|
||||||
|
impl Sentinel for MyResponder {
|
||||||
|
fn abort(r: &Rocket<Ignite>) -> bool {
|
||||||
|
!r.catchers().any(|c| c.code == Some(400)) || r.state::<T>().is_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[sentinels]: @api/rocket/trait.Sentinel.html
|
||||||
|
[`Sentinel`]: @api/rocket/trait.Sentinel.html
|
||||||
|
[`&State<T>`]: @api/rocket/struct.State.html
|
||||||
|
|
||||||
|
### More Typed URIs
|
||||||
|
|
||||||
|
Rocket v0.5 brings a completely overhauled [`uri!()`] macro and support for
|
||||||
|
typed URIs in more APIs. Notably, the `uri!()` macro now:
|
||||||
|
|
||||||
|
* Allows URIs to be constructed from static values:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use rocket::uri;
|
||||||
|
use rocket::http::uri::Absolute;
|
||||||
|
|
||||||
|
const HOST: Absolute<'static> = uri!("http://localhost:8000");
|
||||||
|
```
|
||||||
|
|
||||||
|
* Allows static and dynamic [prefixes and suffixes] to route URIs to be
|
||||||
|
specified:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use rocket::{uri, get};
|
||||||
|
|
||||||
|
#[get("/person/<name>?<age>")]
|
||||||
|
fn person(name: &str, age: Option<u8>) { }
|
||||||
|
|
||||||
|
let uri = uri!("https://rocket.rs/", person("Bob", Some(28)), "#woo");
|
||||||
|
assert_eq!(uri.to_string(), "https://rocket.rs/person/Bob?age=28#woo");
|
||||||
|
|
||||||
|
let host = uri!("http://bob.me");
|
||||||
|
let uri = uri!(host, person("Bob", Some(28)));
|
||||||
|
assert_eq!(uri.to_string(), "http://bob.me/person/Bob?age=28");
|
||||||
|
```
|
||||||
|
|
||||||
|
APIs like [`Redirect`] and [`Client`] now accept typed URIs:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# #[macro_use] extern crate rocket;
|
||||||
|
|
||||||
|
use rocket::response::Redirect;
|
||||||
|
|
||||||
|
#[get("/bye/<name>/<age>")]
|
||||||
|
fn bye(name: &str, age: u8) -> Redirect {
|
||||||
|
Redirect::to(uri!("https://rocket.rs", bye(name, age), "?bye#now"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
use rocket::local::blocking::Client;
|
||||||
|
|
||||||
|
let client = Client::new(rocket::build());
|
||||||
|
let r = client.get(uri!(super::bye("Bob", 30))).dispatch();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[URI types] have been overhauled accordingly. A new [`Reference`] type encodes
|
||||||
|
URI-references. Additionally, all URI types are now `Serialize` and
|
||||||
|
`Deserialize`, allowing URIs to be used in configuration and passed over the
|
||||||
|
wire.
|
||||||
|
|
||||||
|
[`Redirect`]: @api/rocket/response/struct.Redirect.html
|
||||||
|
[`Client`]: @api/rocket/local/index.html
|
||||||
|
[prefixes and suffixes]: @api/rocket/macro.uri.html#prefixes-and-suffixes
|
||||||
|
[`uri!()`]: @api/rocket/macro.uri.html
|
||||||
|
[URI types]: @api/rocket/http/uri/index.html
|
||||||
|
[`Reference`]: @api/rocket/http/uri/struct.Reference.html
|
||||||
|
|
||||||
|
### Real-Time Streams
|
||||||
|
|
||||||
|
Rocket v0.5 introduces real-time, typed, `async` [streams]. The new [async
|
||||||
|
streams] section of the guide contains further details, and we encourage all
|
||||||
|
interested parties to see the new real-time, multi-room [chat example].
|
||||||
|
|
||||||
|
As a taste of what's possible, the following `stream` route emits a `"ping"`
|
||||||
|
Server-Sent Event every `n` seconds, defaulting to `1`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use rocket::*;
|
||||||
|
use rocket::response::stream::{Event, EventStream};;
|
||||||
|
use rocket::tokio::time::{interval, Duration};
|
||||||
|
|
||||||
|
#[get("/ping?<n>")]
|
||||||
|
fn stream(n: Option<u64>) -> EventStream![] {
|
||||||
|
EventStream! {
|
||||||
|
let mut timer = interval(Duration::from_secs(n.unwrap_or(1)));
|
||||||
|
loop {
|
||||||
|
yield Event::data("ping");
|
||||||
|
timer.tick().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[streams]: @api/rocket/response/stream/index.html
|
||||||
|
[async streams]: ../responses/#async-streams
|
||||||
|
[chat example]: @example/chat
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
If you run into any issues upgrading, we encourage you to ask questions via
|
||||||
|
[GitHub discussions] or via chat at [`#rocket:mozilla.org`] on Matrix or the
|
||||||
|
bridged [`#rocket`] IRC channel at `irc.libera.chat`.
|
||||||
|
|
||||||
|
[GitHub discussions]: https://github.com/SergioBenitez/Rocket/discussions
|
||||||
|
[`#rocket:mozilla.org`]: https://chat.mozilla.org/#/room/#rocket:mozilla.org
|
||||||
|
[`#rocket`]: https://kiwiirc.com/client/irc.libera.chat/#rocket
|
646
site/guide/12-faq.md
Normal file
646
site/guide/12-faq.md
Normal file
@ -0,0 +1,646 @@
|
|||||||
|
# FAQ
|
||||||
|
|
||||||
|
Below you'll find a collection of commonly asked questions and answers. If you
|
||||||
|
have suggestions for questions you'd like to see answered here, [comment on the
|
||||||
|
discussion thread].
|
||||||
|
|
||||||
|
[comment on the discussion thread]: https://github.com/SergioBenitez/Rocket/discussions/1836
|
||||||
|
|
||||||
|
## About Rocket
|
||||||
|
|
||||||
|
<details id="monolithic">
|
||||||
|
<summary>
|
||||||
|
Is Rocket a monolithic framework like Rails? Or is it more like Flask?
|
||||||
|
<a class="headerlink" href="#monolithic" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
Neither!
|
||||||
|
|
||||||
|
Rocket's core is small yet complete when it comes to security and correctness.
|
||||||
|
It mainly consists of:
|
||||||
|
|
||||||
|
* Guard traits like [`FromRequest`] and [`FromData`].
|
||||||
|
* Derive macros for all commonly used traits.
|
||||||
|
* Attribute macros for routing.
|
||||||
|
* Thorough compile and launch-time checking.
|
||||||
|
* Optional features to enable functionality like TLS, secrets, and so on.
|
||||||
|
* Zero-copy parsers and validators for common formats like multipart and SSE.
|
||||||
|
* Syntax sugar extensions for features like async streams and traits.
|
||||||
|
|
||||||
|
The goal is for functionality like templating, sessions, ORMs, and so on to be
|
||||||
|
implemented entirely outside of Rocket and yet feel first-class. Indeed, crates
|
||||||
|
like [`rocket_dyn_templates`] and [`rocket_db_pools`] do just this.
|
||||||
|
|
||||||
|
As a result, Rocket is neither "bare-bones" nor is it a kitchen sink for all
|
||||||
|
possible features. Unlike other frameworks in the Rust ecosystem, Rocket makes
|
||||||
|
it its mission to help you avoid security and correctness blunders
|
||||||
|
out-of-the-box. It does this by including, out-of-the-box:
|
||||||
|
|
||||||
|
* A flexible, type-based configuration system.
|
||||||
|
* Security and privacy headers by default.
|
||||||
|
* Zero-Copy RFC compliant URI parsers.
|
||||||
|
* Safe, typed URIs with compile-time checking.
|
||||||
|
* Thorough compile-time and launch-time checking of routes.
|
||||||
|
* A complete testing framework with sync and `async` variants.
|
||||||
|
* Safe, exclusive access to fully decoded HTTP values.
|
||||||
|
* Mandatory data limits to prevent trivial DoS attacks.
|
||||||
|
|
||||||
|
Of course, this functionality comes at a compile-time cost (but notably, _not_
|
||||||
|
at a runtime cost), impacting Rocket's clean build-time. For comparison, here's
|
||||||
|
what building "Hello, world!" for the first time in popular Rust web frameworks
|
||||||
|
looks like:
|
||||||
|
|
||||||
|
| Framework | Dependencies | Build Time |
|
||||||
|
|----------------------|--------------|------------|
|
||||||
|
| Rocket 0.5-rc.2 | 148 | 44s |
|
||||||
|
| Actix-Web 4.0-beta.8 | 175 | 47s |
|
||||||
|
| Tide 0.16 | 209 | 34s |
|
||||||
|
| Warp 0.3 | 148 | 37s |
|
||||||
|
|
||||||
|
<small>· Measurements taken on a MacBookPro15,1 Intel Core i9 @ 2.9GHZ, macOS
|
||||||
|
11.2.1, Rust 1.53 stable. Best of 3.</small><br />
|
||||||
|
<small>· Rocket includes features like multipart parsing and static file
|
||||||
|
serving that would require additional deps in other frameworks.</small>
|
||||||
|
|
||||||
|
Of course, iterative build-time is nearly identical for all frameworks, and the
|
||||||
|
time can be further reduced by using faster linkers like `lld`. We think the
|
||||||
|
trade-off is worth it. Rocket will never compromise security, correctness, or
|
||||||
|
usability to "win" at benchmarks of any sort.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[`rocket_dyn_templates`]: @api/rocket_dyn_templates
|
||||||
|
[`rocket_db_pools`]: @api/rocket_db_pools
|
||||||
|
|
||||||
|
<details id="compact">
|
||||||
|
<summary>
|
||||||
|
I want a small and compact web framework. Is Rocket it?
|
||||||
|
<a class="headerlink" href="#compact" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
We think so! See ["Is Rocket a monolithic framework like Rails?"](#monolithic)
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details id="complete">
|
||||||
|
<summary>
|
||||||
|
I want a web framework with all the bells and whistles. Is Rocket it?
|
||||||
|
<a class="headerlink" href="#complete" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
We think so! See ["Is Rocket a monolithic framework like Rails?"](#monolithic)
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details id="in-prod">
|
||||||
|
<summary>
|
||||||
|
Can I use Rocket in production? Should I? It's only v0.x!
|
||||||
|
<a class="headerlink" href="#in-prod" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
We **enthusiastically** recommend using Rocket in production, with the following
|
||||||
|
caveats:
|
||||||
|
|
||||||
|
1. Run Rocket behind a reverse proxy like HAProxy or in a production load
|
||||||
|
balancing environment. Rocket (Hyper) doesn't employ any defenses against
|
||||||
|
DDoS attacks or certain DoS attacks.
|
||||||
|
|
||||||
|
2. Use a TLS termination proxy for zero-downtown certificate rotation.
|
||||||
|
|
||||||
|
3. Properly configure your databases and database pools, especially with
|
||||||
|
respect to the pool size.
|
||||||
|
|
||||||
|
4. Ensure no blocking I/O happens outside of `spawn_blocking()` invocations.
|
||||||
|
|
||||||
|
While Rocket _is_ still in the `0.x` phase, the version number is purely a
|
||||||
|
stylistic choice. In fact, we consider Rocket to be the most mature web
|
||||||
|
framework in the Rust ecosystem. To our knowledge, Rocket is the only Rust web
|
||||||
|
framework that correctly implements:
|
||||||
|
|
||||||
|
* Server-Sent Events
|
||||||
|
* Graceful Shutdown
|
||||||
|
* Form Parsing with Arbitrarily Structure
|
||||||
|
* Zero-Copy, RFC Conforming URI Types
|
||||||
|
* Ambiguity-Free Routing
|
||||||
|
* Streamed Multipart Uploads
|
||||||
|
|
||||||
|
If you're coming from a different ecosystem, you should feel comfortable
|
||||||
|
considering Rocket's `v0.x` as someone else's `vx.0`. Rust and Cargo's semver
|
||||||
|
policy, and Rocket's strict adherence to it, ensures that Rocket will _never_
|
||||||
|
break your application when upgrading from `0.x.y` to `0.x.z`, where `z >= y`.
|
||||||
|
Furthermore, we backport _all_ security and correctness patches to the previous
|
||||||
|
major release (`0.{x-1}.y`), so your application remains secure if you need time
|
||||||
|
to upgrade.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details id="performance">
|
||||||
|
<summary>
|
||||||
|
Is Rocket slow? Is Rocket fast?
|
||||||
|
<a class="headerlink" href="#performance" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
Rocket is pretty fast.
|
||||||
|
|
||||||
|
A commonly repeated myth is that Rocket's great usability comes at the cost of
|
||||||
|
runtime performance. _**This is false.**_ Rocket's usability derives largely
|
||||||
|
from compile-time checks with _zero_ bearing on runtime performance.
|
||||||
|
|
||||||
|
So what about benchmarks? Well, benchmarking is _hard_, and besides often being
|
||||||
|
conducted incorrectly, often appear to say more than they do. So, when you see a
|
||||||
|
benchmark for "Hello, world!", you should know that the benchmark's relevance
|
||||||
|
doesn't extend far beyond "Hello, world!" servers and the specific way the
|
||||||
|
measurement was taken. In other words, they provide only a baseline truth that
|
||||||
|
is hard to extrapolate to real-world use-cases, _your_ use-case.
|
||||||
|
|
||||||
|
Nevertheless, here are some things you can consider as _generally_ true about
|
||||||
|
Rocket applications:
|
||||||
|
|
||||||
|
* They'll perform much, _much_ better than those written in scripting
|
||||||
|
languages like Python or Ruby.
|
||||||
|
* They'll perform much better than those written in VM or JIT languages like
|
||||||
|
JavaScript or Java.
|
||||||
|
* They'll perform a bit better than those written in compiled but GC'd
|
||||||
|
languages like Go.
|
||||||
|
* They'll perform competitively with those written in compiled, non-GC'd
|
||||||
|
languages like Rust or C.
|
||||||
|
|
||||||
|
Again, we emphasize _generally_ true. It is trivial to write a Rocket
|
||||||
|
application that is slower than a similar Python application.
|
||||||
|
|
||||||
|
Besides a framework's _internal_ performance, you should also consider whether
|
||||||
|
it enables your _application itself_ to perform well. Rocket takes great care to
|
||||||
|
enable your application to perform as little work as possible through
|
||||||
|
unique-to-Rocket features like [managed state], [request-local state], and
|
||||||
|
zero-copy parsing and deserialization.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[managed state]: ../state/#managed-state
|
||||||
|
[request-local state]: ../state/#request-local-state
|
||||||
|
|
||||||
|
<details id="showcase">
|
||||||
|
<summary>
|
||||||
|
What are some examples of "big" apps written in Rocket?
|
||||||
|
<a class="headerlink" href="#showcase" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
Here are some notable projects and websites in Rocket we're aware of:
|
||||||
|
|
||||||
|
* [Vaultwarden] - A BitWarden Server
|
||||||
|
* [Conduit] - A Matrix Homeserver
|
||||||
|
* [Rust-Lang.org] - Rust Language Website
|
||||||
|
* [Plume] - Federated Blogging Engine
|
||||||
|
* [Hagrid] - OpenPGP KeyServer ([keys.openpgp.org](https://keys.openpgp.org/))
|
||||||
|
* [SourceGraph Syntax Highlighter] - Syntax Highlighting API
|
||||||
|
|
||||||
|
[Let us know] if you have a notable, public facing application written in Rocket
|
||||||
|
you'd like to see here!
|
||||||
|
|
||||||
|
[Vaultwarden]: https://github.com/dani-garcia/vaultwarden
|
||||||
|
[Conduit]: https://conduit.rs/
|
||||||
|
[Rust-Lang.org]: https://www.rust-lang.org/
|
||||||
|
[Plume]: https://github.com/Plume-org/Plume
|
||||||
|
[Hagrid]: https://gitlab.com/hagrid-keyserver/hagrid/
|
||||||
|
[SourceGraph Syntax Highlighter]: https://github.com/sourcegraph/syntect_server
|
||||||
|
[Let us know]: https://github.com/SergioBenitez/Rocket/discussions/categories/show-and-tell
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details id="releases">
|
||||||
|
<summary>
|
||||||
|
When will version `$y` be released? Why does it take so long?
|
||||||
|
<a class="headerlink" href="#releases" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
Rocket represents an ecosystem-wide effort to create a web framework that
|
||||||
|
enables writing web applications with unparalleled security, performance, and
|
||||||
|
usability. From design to implementation to documentation, Rocket is carefully
|
||||||
|
crafted to ensure the greatest productivity and reliability with the fewest
|
||||||
|
surprises. Our goal is to make Rocket the obvious choice across _all_ languages.
|
||||||
|
|
||||||
|
Accomplishing this takes time, and our efforts extend to the entire ecosystem.
|
||||||
|
For example, work for Rocket v0.5 included:
|
||||||
|
|
||||||
|
* [Fixing correctness issues in `x509-parser`.](https://github.com/rusticata/x509-parser/pull/90)
|
||||||
|
* [Reporting multiple](https://github.com/bikeshedder/deadpool/issues/114)
|
||||||
|
[correctness issues](https://github.com/bikeshedder/deadpool/issues/113) in `deadpool`.
|
||||||
|
* [Fixing a major usability issue in `async-stream`.](https://github.com/tokio-rs/async-stream/pull/57)
|
||||||
|
* [Creating a brand new configuration library.](https://github.com/SergioBenitez/Figment)
|
||||||
|
* [Updating](https://github.com/rousan/multer-rs/pull/21),
|
||||||
|
[fixing](https://github.com/rousan/multer-rs/pull/29), and
|
||||||
|
[maintaining](https://github.com/rousan/multer-rs/commit/2758e778e6aa2785b737c82fe45e58026bea2f01) `multer`.
|
||||||
|
* [Significantly improving `async_trait` correctness and usability.](https://github.com/dtolnay/async-trait/pull/143)
|
||||||
|
* [Porting `Pattern` APIs to stable.](https://github.com/SergioBenitez/stable-pattern)
|
||||||
|
* [Porting macro diagnostics to stable.](https://github.com/SergioBenitez/proc-macro2-diagnostics)
|
||||||
|
* [Creating a brand new byte unit library.](https://github.com/SergioBenitez/ubyte)
|
||||||
|
* [Fixing a bug in `rustc`'s `libtest`.](https://github.com/rust-lang/rust/pull/78227)
|
||||||
|
|
||||||
|
A version of Rocket is released whenever it is feature-complete and exceeds
|
||||||
|
feature, security, and usability parity with the previous version. As a result,
|
||||||
|
specifying a release date is nearly impossible. We are _always_ willing to delay
|
||||||
|
a release if these properties are not readily evident.
|
||||||
|
|
||||||
|
We know it can be frustrating, but we hope you'll agree that Rocket is worth the
|
||||||
|
wait.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## How To
|
||||||
|
|
||||||
|
<details id="web-sockets">
|
||||||
|
<summary>
|
||||||
|
Can I, and if so how, do I use WebSockets?
|
||||||
|
<a class="headerlink" href="#web-sockets" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
Rocket doesn't support WebSockets quite yet. We're [working on it].
|
||||||
|
|
||||||
|
That being said, Rocket _does_ suport [Server-Sent Events], which allows for
|
||||||
|
real-time _unidirectional_ communication from the server to the client. This is
|
||||||
|
often sufficient for many of the applications that WebSockets are typically used
|
||||||
|
for. For instance, the [chat example] uses SSE to implement a real-time,
|
||||||
|
multiroom chat application.
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[working on it]: https://github.com/SergioBenitez/Rocket/issues/90
|
||||||
|
[Server-Sent Events]: @api/rocket/response/stream/struct.EventStream.html
|
||||||
|
[chat example]: @example/chat
|
||||||
|
|
||||||
|
<details id="global-state">
|
||||||
|
<summary>
|
||||||
|
Should I use global state via something like `lazy_static!`?
|
||||||
|
<a class="headerlink" href="#global-state" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
No. Rocket's [managed state] provides a better alternative.
|
||||||
|
|
||||||
|
While it may be convenient or comfortable to use global state, the downsides are
|
||||||
|
numerous. They include:
|
||||||
|
|
||||||
|
* The inability to test your application with different state.
|
||||||
|
* The inability to run your application on different threads with different
|
||||||
|
state.
|
||||||
|
* The inability to know the state a route accesses by looking at its
|
||||||
|
signature.
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[managed state]: ../state/#managed-state
|
||||||
|
|
||||||
|
<details id="file-uploads">
|
||||||
|
<summary>
|
||||||
|
How do I handle file uploads? What is this "multipart" in my stream?
|
||||||
|
<a class="headerlink" href="#file-uploads" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
For a quick example on how to handle file uploads, see [multipart forms].
|
||||||
|
|
||||||
|
File uploads are transmitted by the browser as [multipart] form submissions,
|
||||||
|
which Rocket handles natively as a [`DataField`]. The [`TempFile`] form guard
|
||||||
|
can accept a `DataField` and stream the data to disk to then be persisted.
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[multipart]: https://datatracker.ietf.org/doc/html/rfc7578
|
||||||
|
[multipart forms]: ../requests/#multipart
|
||||||
|
[`DataField`]: @api/rocket/form/struct.DataField.html
|
||||||
|
[`TempFile`]: @api/rocket/fs/enum.TempFile.html
|
||||||
|
|
||||||
|
<details id="raw-request">
|
||||||
|
<summary>
|
||||||
|
How do I get an `&Request` in a handler?
|
||||||
|
<a class="headerlink" href="#raw-request" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
You don't!
|
||||||
|
|
||||||
|
Rocket's [philosophy] is that as much of the request should be validated and
|
||||||
|
converted into useful typed values _before_ being processed. Allowing a
|
||||||
|
`Request` to be handled directly is incompatible with this idea.
|
||||||
|
|
||||||
|
Instead, Rocket's handlers work through _guards_, reified as traits, which
|
||||||
|
validate and extract parts of a request as needed. Rocket automatically invokes
|
||||||
|
these guards for you, so custom guards are write-once-use-everywhere. Rocket
|
||||||
|
won't invoke handlers that depend on guards that fail. This way, handlers only
|
||||||
|
deal with fully validated, typed, secure values.
|
||||||
|
|
||||||
|
Rocket provides all of the guard implementations you would expect
|
||||||
|
out-of-the-box, and you can implement your own, too. See the following:
|
||||||
|
|
||||||
|
* Parameter Guards: [`FromParam`]
|
||||||
|
* Multi-Segment Guards: [`FromSegments`]
|
||||||
|
* Data Guards: [`FromData`]
|
||||||
|
* Form Guards: [`FromFrom`]
|
||||||
|
* Request Guards: [`FromRequest`]
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[philosophy]: ../introduction/#foreword
|
||||||
|
[`FromParam`]: @api/rocket/request/trait.FromParam.html
|
||||||
|
[`FromSegments`]: @api/rocket/request/trait.FromSegments.html
|
||||||
|
[`FromData`]: @api/rocket/data/trait.FromData.html
|
||||||
|
[`FromFrom`]: @api/rocket/form/trait.FromForm.html
|
||||||
|
[`FromRequest`]: @api/rocket/request/trait.FromRequest.html
|
||||||
|
|
||||||
|
<details id="response-headers">
|
||||||
|
<summary>
|
||||||
|
How do I add a header to a response?
|
||||||
|
<a class="headerlink" href="#response-headers" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
That depends on the header!
|
||||||
|
|
||||||
|
Any "transport" headers (`Content-Length`, `Transfer-Encoding`, etc.) are
|
||||||
|
automatically set by Rocket and cannot be directly overridden for correctness
|
||||||
|
reasons. The rest are set by a type's [`Responder`] implementation.
|
||||||
|
|
||||||
|
**Status**
|
||||||
|
|
||||||
|
Rocket automatically sets a Status header for all responses. If the `Responder`
|
||||||
|
doesn't explicitly set a status, it defaults to `200`. Responders like
|
||||||
|
`Option<T>`, however, _do_ set the status. See the [`Responder`] docs for
|
||||||
|
details, and the [`status`] module for details on setting a custom Status or
|
||||||
|
overriding an existing one.
|
||||||
|
|
||||||
|
**Content-Type**
|
||||||
|
|
||||||
|
Rocket automatically sets a Content-Type header for most types it implements
|
||||||
|
`Responder` for, so in the common case, there's nothing to do. This includes
|
||||||
|
types like `&str`, `&[u8]`, `NamedFile`, and so on. The [`content`] module docs
|
||||||
|
have details on setting a custom Content-Type or overriding an existing one.
|
||||||
|
|
||||||
|
**Everything Else**
|
||||||
|
|
||||||
|
To add a custom header, you'll need a custom [`Responder`]. Not to worry!
|
||||||
|
[`Responder` can be derived](@api/rocket/derive.Responder.html) in almost all
|
||||||
|
cases. If a type for the header you want to add already exists, you can directly
|
||||||
|
derive `Responder` for a struct that contains the header value, which adds the
|
||||||
|
header to the response:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# #[macro_use] extern crate rocket;
|
||||||
|
# use rocket::http::Header;
|
||||||
|
|
||||||
|
# type HeaderType = Header<'static>;
|
||||||
|
|
||||||
|
# impl<T> From<T> for MyResponder<T> {
|
||||||
|
# fn from(inner: T) -> Self {
|
||||||
|
# MyResponder { inner, header: Header::new("X-My-Header", "some value") }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
#[derive(Responder)]
|
||||||
|
struct MyResponder<T> {
|
||||||
|
inner: T,
|
||||||
|
header: HeaderType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn with_header() -> MyResponder<&'static str> {
|
||||||
|
MyResponder::from("Hello, world!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A `HeaderType` won't exist for custom headers, but you can define your own type.
|
||||||
|
As long as it implements `Into<Header>` for Rocket's [`Header`], the type can be
|
||||||
|
used as a field in derived struct.
|
||||||
|
|
||||||
|
You can always implement `Responder` directly. Make sure to leverage existing
|
||||||
|
responders in your implementation. For example, _don't_ serialize JSON manually.
|
||||||
|
Instead, use the existing [`Json`] responder, like in the example below:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# #[derive(rocket::serde::Serialize)]
|
||||||
|
# #[serde(crate = "rocket::serde")]
|
||||||
|
# struct Person { name: String, age: usize };
|
||||||
|
|
||||||
|
use rocket::request::Request;
|
||||||
|
use rocket::response::{self, Response, Responder};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
impl<'r> Responder<'r, 'static> for Person {
|
||||||
|
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
|
||||||
|
Response::build_from(Json(&self).respond_to(req)?)
|
||||||
|
.raw_header("X-Person-Name", self.name)
|
||||||
|
.raw_header("X-Person-Age", self.age.to_string())
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[`Responder`]: @api/rocket/response/trait.Responder.html
|
||||||
|
[`content`]: @api/rocket/response/content/index.html
|
||||||
|
[`status`]: @api/rocket/response/status/index.html
|
||||||
|
[`Header`]: @api/rocket/http/struct.Header.html
|
||||||
|
[`Json`]: @api/rocket/serde/json/struct.Json.html
|
||||||
|
|
||||||
|
<details id="multiple-responses">
|
||||||
|
<summary>
|
||||||
|
How do I make one handler return different responses or status codes?
|
||||||
|
<a class="headerlink" href="#multiple-responses" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
If you're returning _two_ different responses, use a `Result<T, E>` or an
|
||||||
|
[`Either<A, B>`].
|
||||||
|
|
||||||
|
If you need to return _more_ than two kinds, [derive a custom `Responder`] `enum`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use rocket::response::Responder;
|
||||||
|
use rocket::fs::NamedFile;
|
||||||
|
use rocket::http::ContentType;
|
||||||
|
|
||||||
|
#[derive(Responder)]
|
||||||
|
enum Error<'r, T> {
|
||||||
|
#[response(status = 400)]
|
||||||
|
Unauthorized(T),
|
||||||
|
#[response(status = 404)]
|
||||||
|
NotFound(NamedFile),
|
||||||
|
#[response(status = 500)]
|
||||||
|
A(&'r str, ContentType),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[`Either<A, B>`]: https://docs.rs/either/1/either/enum.Either.html
|
||||||
|
[derive a custom `Responder`]: @api/rocket/derive.Responder.html
|
||||||
|
|
||||||
|
<details id="automatic-reload">
|
||||||
|
<summary>
|
||||||
|
How do I make Rocket reload automatically when I change source code?
|
||||||
|
<a class="headerlink" href="#automatic-reload" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
In debug mode, Rocket automatically reloads templates for you. So if all you
|
||||||
|
need is live template reloading, Rocket's got you covered.
|
||||||
|
|
||||||
|
For everything else, you'll need to use an external tool like [`cargo-watch`],
|
||||||
|
[`watchexec`] or [`entr`]. With `cargo-watch`, you can automatically rebuild and
|
||||||
|
run a Rocket application by executing:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo watch -x run
|
||||||
|
```
|
||||||
|
|
||||||
|
To only restart on successful compilations, see [this note].
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[`cargo-watch`]: https://github.com/watchexec/cargo-watch
|
||||||
|
[`watchexec`]: https://github.com/watchexec/watchexec
|
||||||
|
[`entr`]: http://eradman.com/entrproject/
|
||||||
|
[this note]: https://github.com/watchexec/cargo-watch/tree/b75ce2c260874dea480f4accfd46ab28709ec56a#restarting-an-application-only-if-the-buildcheck-succeeds
|
||||||
|
|
||||||
|
<details id="external-managed-state">
|
||||||
|
<summary>
|
||||||
|
How do I access managed state outside of a Rocket-related context?
|
||||||
|
<a class="headerlink" href="#external-managed-state" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
Use an `Arc`, like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use rocket::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[launch]
|
||||||
|
fn rocket() -> _ {
|
||||||
|
# struct MyState;
|
||||||
|
let state = Arc::new(MyState);
|
||||||
|
|
||||||
|
let external = state.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let use_state = external;
|
||||||
|
});
|
||||||
|
|
||||||
|
rocket::build().manage(state)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details id="internal-server">
|
||||||
|
<summary>
|
||||||
|
How do I make Rocket a _part_ of my application as opposed to the whole thing?
|
||||||
|
<a class="headerlink" href="#internal-server" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
If you're developing an application where an HTTP server is a _part_ of the
|
||||||
|
application instead of being the entire thing, use the `#[main]` attribute and
|
||||||
|
manually call [`launch()`]:
|
||||||
|
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
#[rocket::main]
|
||||||
|
async fn main() {
|
||||||
|
# let should_start_server = false;
|
||||||
|
if should_start_server {
|
||||||
|
let result = rocket::build().launch().await;
|
||||||
|
} else {
|
||||||
|
// do something else
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The cost to using the attribute is imperceptible and guarantees compatibility
|
||||||
|
with Rocket's async I/O.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
[`launch()`]: @api/rocket/struct.Rocket.html#method.launch
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
<details id="broken-example">
|
||||||
|
<summary>
|
||||||
|
Is example `foo` broken? It doesn't work for me.
|
||||||
|
<a class="headerlink" href="#broken-example" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
Almost certainly not.
|
||||||
|
|
||||||
|
Every example and code snippet you see in published documentation is tested by
|
||||||
|
the CI on every commit, and we only publish docs that pass the CI. Unless the CI
|
||||||
|
environment is broken, the examples _cannot_ be wrong.
|
||||||
|
|
||||||
|
Common mistakes when running examples include:
|
||||||
|
|
||||||
|
* Looking at an example for version `y` but depending on version `x`. Select
|
||||||
|
the proper git tag!
|
||||||
|
* Looking at outdated examples on StackOverflow or Google. Check the
|
||||||
|
date/version!
|
||||||
|
* Not configuring the correct dependencies. See the example's `Cargo.toml`!
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details id="unsat-bound">
|
||||||
|
<summary>
|
||||||
|
The trait bound `rocket::Responder` (`FromRequest`, etc.) is not satisfied.
|
||||||
|
<a class="headerlink" href="#unsat-bound" title="permalink">#</a>
|
||||||
|
</summary>
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
If you're fairly certain a type implements a given Rocket trait but still get an
|
||||||
|
error like:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
error[E0277]: the trait bound `Foo: Responder<'_, '_>` is not satisfied
|
||||||
|
--> src\main.rs:4:20
|
||||||
|
|
|
||||||
|
4 | fn foo() -> Foo
|
||||||
|
| ^^^ the trait `Responder<'_, '_>` is not implemented for `Foo`
|
||||||
|
|
|
||||||
|
= note: required by `respond_to`
|
||||||
|
```
|
||||||
|
|
||||||
|
...then you're almost certainly depending on libraries which depend on different
|
||||||
|
versions of `rocket`. A common mistake is to depend on a `contrib` library from
|
||||||
|
git while also depending on a `crates.io` version of Rocket or vice-versa:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
rocket = "0.5.0-rc.1"
|
||||||
|
rocket_db_pools = { git = "https://github.com/SergioBenitez/Rocket.git" }
|
||||||
|
```
|
||||||
|
|
||||||
|
This is _never_ correct. In Rust, types from two different versions of a library
|
||||||
|
or from different providers (like `git` vs. `crates.io`) are _always_ considered
|
||||||
|
distinct, even if they have the same name. Therefore, even if a type implements
|
||||||
|
a trait from one library, it _does not_ implement the trait from the other
|
||||||
|
library (since it is considered to be a _different_, _distinct_ library). In
|
||||||
|
other words, you can _never_ mix two different published versions of Rocket, a
|
||||||
|
published version and a `git` version, or two instances from different `git`
|
||||||
|
revisions.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</details>
|
@ -1028,8 +1028,8 @@ you might write:
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #[macro_use] extern crate rocket;
|
# #[macro_use] extern crate rocket;
|
||||||
extern crate time;
|
|
||||||
|
|
||||||
|
use rocket::time::Date;
|
||||||
use rocket::form::{self, Error};
|
use rocket::form::{self, Error};
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
@ -1038,10 +1038,10 @@ struct CreditCard {
|
|||||||
number: u64,
|
number: u64,
|
||||||
#[field(validate = range(..9999))]
|
#[field(validate = range(..9999))]
|
||||||
cvv: u16,
|
cvv: u16,
|
||||||
expiration: time::Date,
|
expiration: Date,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn luhn<'v>(number: &u64, cvv: u16, exp: &time::Date) -> form::Result<'v, ()> {
|
fn luhn<'v>(number: &u64, cvv: u16, exp: &Date) -> form::Result<'v, ()> {
|
||||||
# let valid = false;
|
# let valid = false;
|
||||||
if !valid {
|
if !valid {
|
||||||
Err(Error::validation("invalid credit card number"))?;
|
Err(Error::validation("invalid credit card number"))?;
|
||||||
|
@ -352,14 +352,14 @@ returns an infinite [`TextStream`] that produces one `"hello"` every second:
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
# use rocket::get;
|
# use rocket::get;
|
||||||
use rocket::tokio::time::{self, Duration};
|
use rocket::tokio::time::{Duration, interval};
|
||||||
use rocket::response::stream::TextStream;
|
use rocket::response::stream::TextStream;
|
||||||
|
|
||||||
/// Produce an infinite series of `"hello"`s, one per second.
|
/// Produce an infinite series of `"hello"`s, one per second.
|
||||||
#[get("/infinite-hellos")]
|
#[get("/infinite-hellos")]
|
||||||
fn hello() -> TextStream![&'static str] {
|
fn hello() -> TextStream![&'static str] {
|
||||||
TextStream! {
|
TextStream! {
|
||||||
let mut interval = time::interval(Duration::from_secs(1));
|
let mut interval = interval(Duration::from_secs(1));
|
||||||
loop {
|
loop {
|
||||||
yield "hello";
|
yield "hello";
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
|
@ -111,7 +111,7 @@ use rocket::http::{ContentType, Status};
|
|||||||
|
|
||||||
# let rocket = rocket::build().mount("/", routes![hello]);
|
# let rocket = rocket::build().mount("/", routes![hello]);
|
||||||
# let client = Client::debug(rocket).expect("valid rocket instance");
|
# let client = Client::debug(rocket).expect("valid rocket instance");
|
||||||
let mut response = client.get("/").dispatch();
|
let mut response = client.get(uri!(hello)).dispatch();
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.content_type(), Some(ContentType::Plain));
|
assert_eq!(response.content_type(), Some(ContentType::Plain));
|
||||||
@ -187,13 +187,18 @@ Then, we create a new `GET /` request and dispatch it, getting back our
|
|||||||
application's response:
|
application's response:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
# use rocket::uri;
|
||||||
# #[rocket::launch]
|
# #[rocket::launch]
|
||||||
# fn rocket() -> _ {
|
# fn rocket() -> _ {
|
||||||
# rocket::build().configure(rocket::Config::debug_default())
|
# rocket::build().configure(rocket::Config::debug_default())
|
||||||
# }
|
# }
|
||||||
|
|
||||||
|
# #[rocket::get("/")]
|
||||||
|
# fn hello() -> &'static str { "Hello, world!" }
|
||||||
|
|
||||||
# use rocket::local::blocking::Client;
|
# use rocket::local::blocking::Client;
|
||||||
# let client = Client::tracked(rocket()).expect("valid rocket instance");
|
# let client = Client::tracked(rocket()).expect("valid rocket instance");
|
||||||
let mut response = client.get("/").dispatch();
|
let mut response = client.get(uri!(hello)).dispatch();
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, we ensure that the response contains the information we expect it to.
|
Finally, we ensure that the response contains the information we expect it to.
|
||||||
@ -215,7 +220,7 @@ use rocket::http::{ContentType, Status};
|
|||||||
#
|
#
|
||||||
# let rocket = rocket::build().mount("/", routes![hello]);
|
# let rocket = rocket::build().mount("/", routes![hello]);
|
||||||
# let client = Client::debug(rocket).expect("valid rocket instance");
|
# let client = Client::debug(rocket).expect("valid rocket instance");
|
||||||
# let mut response = client.get("/").dispatch();
|
# let mut response = client.get(uri!(hello)).dispatch();
|
||||||
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.into_string(), Some("Hello, world!".into()));
|
assert_eq!(response.into_string(), Some("Hello, world!".into()));
|
||||||
@ -256,7 +261,7 @@ mod test {
|
|||||||
let client = Client::tracked(rocket()).expect("valid rocket instance");
|
let client = Client::tracked(rocket()).expect("valid rocket instance");
|
||||||
# */
|
# */
|
||||||
# let client = Client::debug(rocket()).expect("valid rocket instance");
|
# let client = Client::debug(rocket()).expect("valid rocket instance");
|
||||||
let mut response = client.get("/").dispatch();
|
let mut response = client.get(uri!(super::hello)).dispatch();
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
assert_eq!(response.into_string().unwrap(), "Hello, world!");
|
assert_eq!(response.into_string().unwrap(), "Hello, world!");
|
||||||
}
|
}
|
||||||
|
@ -38,17 +38,16 @@ values:
|
|||||||
Configurations can be arbitrarily namespaced by [`Profile`]s. Rocket's
|
Configurations can be arbitrarily namespaced by [`Profile`]s. Rocket's
|
||||||
[`Config`] and [`Config::figment()`] providers automatically set the
|
[`Config`] and [`Config::figment()`] providers automatically set the
|
||||||
configuration profile to "debug" when compiled in "debug" mode and "release"
|
configuration profile to "debug" when compiled in "debug" mode and "release"
|
||||||
when compiled in release mode. With the exception of `log_level`, which changes
|
when compiled in release mode, but you can arbitrarily name and set profiles to
|
||||||
from `normal` in debug to `critical` in release, all of the default
|
your desire. For example, with the [default provider](#default-provider), you
|
||||||
configuration values are the same in all profiles. What's more, all
|
can set the selected profile via `ROCKET_PROFILE`. This results in Rocket
|
||||||
configuration values _have_ defaults, so no configuration needs to be supplied
|
preferring the values in the `ROCKET_PROFILE` profile.
|
||||||
to get an application going.
|
|
||||||
|
|
||||||
In addition to any profiles you declare, there are two meta-profiles, `default`
|
In addition to any profiles you declare, there are two meta-profiles, `default`
|
||||||
and `global`, which can be used to provide values that apply to _all_ profiles.
|
and `global`, which can be used to provide values that apply to _all_ profiles.
|
||||||
Values provided in a `default` profile are used as fall-back values when the
|
Values provided in a `default` profile are used as fall-back values when the
|
||||||
selected profile doesn't contain a requested values, while values in the
|
selected profile doesn't contain a requested value, while values in the `global`
|
||||||
`global` profile supplant any values with the same name in any profile.
|
profile supplant any values with the same name in any profile.
|
||||||
|
|
||||||
[`Provider`]: @figment/trait.Provider.html
|
[`Provider`]: @figment/trait.Provider.html
|
||||||
[`Profile`]: @figment/struct.Profile.html
|
[`Profile`]: @figment/struct.Profile.html
|
||||||
@ -71,22 +70,23 @@ selected profile doesn't contain a requested values, while values in the
|
|||||||
Rocket's default configuration provider is [`Config::figment()`]; this is the
|
Rocket's default configuration provider is [`Config::figment()`]; this is the
|
||||||
provider that's used when calling [`rocket::build()`].
|
provider that's used when calling [`rocket::build()`].
|
||||||
|
|
||||||
The default figment merges, at a per-key level, and reads from the following
|
The default figment reads from and merges, at a per-key level, the following
|
||||||
sources, in ascending priority order:
|
sources in ascending priority order:
|
||||||
|
|
||||||
1. [`Config::default()`] - which provides default values for all parameters.
|
1. [`Config::default()`], which provides default values for all parameters.
|
||||||
2. `Rocket.toml` _or_ TOML file path in `ROCKET_CONFIG` environment variable.
|
2. `Rocket.toml` _or_ TOML file path in `ROCKET_CONFIG` environment variable.
|
||||||
3. `ROCKET_` prefixed environment variables.
|
3. `ROCKET_` prefixed environment variables.
|
||||||
|
|
||||||
The selected profile is the value of the `ROCKET_PROFILE` environment variable,
|
The selected profile is the value of the `ROCKET_PROFILE` environment variable,
|
||||||
or if it is not set, "debug" when compiled in debug mode and "release" when
|
or if it is not set, "debug" when compiled in debug mode and "release" when
|
||||||
compiled in release mode.
|
compiled in release mode. With the exception of `log_level`, which changes from
|
||||||
|
`normal` in debug to `critical` in release, all of the default configuration
|
||||||
|
values are the same in all profiles. What's more, all configuration values
|
||||||
|
_have_ defaults, so no configuration is needed to get started.
|
||||||
|
|
||||||
As a result, without any effort, Rocket's server can be configured via a
|
As a result of `Config::figment()`, without any effort, Rocket can be configured
|
||||||
`Rocket.toml` file and/or via environment variables, the latter of which take
|
via a `Rocket.toml` file and/or via environment variables, the latter of which
|
||||||
precedence over the former. Note that neither the file nor any environment
|
take precedence over the former.
|
||||||
variables need to be present as [`Config::default()`] is a complete
|
|
||||||
configuration source.
|
|
||||||
|
|
||||||
[`Config::default()`]: @api/rocket/struct.Config.html#method.default
|
[`Config::default()`]: @api/rocket/struct.Config.html#method.default
|
||||||
|
|
||||||
@ -421,7 +421,7 @@ more complex cases.
|
|||||||
crate. As such, you may need to import crates directly:
|
crate. As such, you may need to import crates directly:
|
||||||
|
|
||||||
`
|
`
|
||||||
figment = { version = "0.9", features = ["env", "toml", "json"] }
|
figment = { version = "0.10", features = ["env", "toml", "json"] }
|
||||||
`
|
`
|
||||||
|
|
||||||
As a first example, we override configuration values at runtime by merging
|
As a first example, we override configuration values at runtime by merging
|
||||||
|
@ -30,6 +30,8 @@ aspect of Rocket. The sections are:
|
|||||||
Rocket.
|
Rocket.
|
||||||
- **[Conclusion](conclusion/):** concludes the guide and discusses next steps
|
- **[Conclusion](conclusion/):** concludes the guide and discusses next steps
|
||||||
for learning.
|
for learning.
|
||||||
|
- **[FAQ](faq/):** answers to frequently asked questions about Rocket and
|
||||||
|
using it.
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ rocket = { path = "../../core/lib", features = ["secrets", "json"] }
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
figment = { version = "0.10", features = ["toml", "env"] }
|
figment = { version = "0.10", features = ["toml", "env"] }
|
||||||
time = "0.2"
|
|
||||||
|
|
||||||
[dev-dependencies.rocket_dyn_templates]
|
[dev-dependencies.rocket_dyn_templates]
|
||||||
path = "../../contrib/dyn_templates"
|
path = "../../contrib/dyn_templates"
|
||||||
|
Loading…
Reference in New Issue
Block a user