Add 0.5 migration guide, FAQ to site docs.

This commit is contained in:
Sergio Benitez 2021-08-19 19:43:05 -07:00
parent 3616f25c0b
commit a361bbd899
8 changed files with 1486 additions and 27 deletions

View 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
View 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>

View File

@ -1028,8 +1028,8 @@ you might write:
```rust
# #[macro_use] extern crate rocket;
extern crate time;
use rocket::time::Date;
use rocket::form::{self, Error};
#[derive(FromForm)]
@ -1038,10 +1038,10 @@ struct CreditCard {
number: u64,
#[field(validate = range(..9999))]
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;
if !valid {
Err(Error::validation("invalid credit card number"))?;

View File

@ -352,14 +352,14 @@ returns an infinite [`TextStream`] that produces one `"hello"` every second:
```rust
# use rocket::get;
use rocket::tokio::time::{self, Duration};
use rocket::tokio::time::{Duration, interval};
use rocket::response::stream::TextStream;
/// Produce an infinite series of `"hello"`s, one per second.
#[get("/infinite-hellos")]
fn hello() -> TextStream![&'static str] {
TextStream! {
let mut interval = time::interval(Duration::from_secs(1));
let mut interval = interval(Duration::from_secs(1));
loop {
yield "hello";
interval.tick().await;

View File

@ -111,7 +111,7 @@ use rocket::http::{ContentType, Status};
# let rocket = rocket::build().mount("/", routes![hello]);
# 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.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:
```rust
# use rocket::uri;
# #[rocket::launch]
# fn rocket() -> _ {
# rocket::build().configure(rocket::Config::debug_default())
# }
# #[rocket::get("/")]
# fn hello() -> &'static str { "Hello, world!" }
# use rocket::local::blocking::Client;
# 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.
@ -215,7 +220,7 @@ use rocket::http::{ContentType, Status};
#
# let rocket = rocket::build().mount("/", routes![hello]);
# 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.into_string(), Some("Hello, world!".into()));
@ -256,7 +261,7 @@ mod test {
let client = Client::tracked(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.into_string().unwrap(), "Hello, world!");
}

View File

@ -38,17 +38,16 @@ values:
Configurations can be arbitrarily namespaced by [`Profile`]s. Rocket's
[`Config`] and [`Config::figment()`] providers automatically set the
configuration profile to "debug" when compiled in "debug" mode and "release"
when 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 needs to be supplied
to get an application going.
when compiled in release mode, but you can arbitrarily name and set profiles to
your desire. For example, with the [default provider](#default-provider), you
can set the selected profile via `ROCKET_PROFILE`. This results in Rocket
preferring the values in the `ROCKET_PROFILE` profile.
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.
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
`global` profile supplant any values with the same name in any profile.
selected profile doesn't contain a requested value, while values in the `global`
profile supplant any values with the same name in any profile.
[`Provider`]: @figment/trait.Provider.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
provider that's used when calling [`rocket::build()`].
The default figment merges, at a per-key level, and reads from the following
sources, in ascending priority order:
The default figment reads from and merges, at a per-key level, the following
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.
3. `ROCKET_` prefixed environment variables.
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
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
`Rocket.toml` file and/or via environment variables, the latter of which take
precedence over the former. Note that neither the file nor any environment
variables need to be present as [`Config::default()`] is a complete
configuration source.
As a result of `Config::figment()`, without any effort, Rocket can be configured
via a `Rocket.toml` file and/or via environment variables, the latter of which
take precedence over the former.
[`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:
`
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

View File

@ -30,6 +30,8 @@ aspect of Rocket. The sections are:
Rocket.
- **[Conclusion](conclusion/):** concludes the guide and discusses next steps
for learning.
- **[FAQ](faq/):** answers to frequently asked questions about Rocket and
using it.
## Getting Help

View File

@ -13,7 +13,6 @@ rocket = { path = "../../core/lib", features = ["secrets", "json"] }
serde = { version = "1.0", features = ["derive"] }
rand = "0.8"
figment = { version = "0.10", features = ["toml", "env"] }
time = "0.2"
[dev-dependencies.rocket_dyn_templates]
path = "../../contrib/dyn_templates"