mirror of https://github.com/rwf2/Rocket.git
395 lines
14 KiB
Markdown
395 lines
14 KiB
Markdown
# Rocket v0.2: Managed State & More
|
|
|
|
<p class="metadata"><strong>
|
|
Posted by <a href="https://sergio.bz">Sergio Benitez</a> on February 06, 2017
|
|
</strong></p>
|
|
|
|
<!-- we should be able to generate the title and subheading -->
|
|
|
|
Today marks the first major release since Rocket's debut a little over a month
|
|
ago. Rocket v0.2 packs a ton of new features, fixes, and general improvements.
|
|
Much of the development in v0.2 was led by the community, either through reports
|
|
via the [GitHub issue tracker](https://github.com/rwf2/Rocket/issues)
|
|
or via direct contributions. In fact, there have been **20 unique contributors**
|
|
to Rocket's codebase since Rocket's initial introduction! Community feedback has
|
|
been incredible. As a special thank you, we include the names of these
|
|
contributors at the end of this article.
|
|
|
|
## About Rocket
|
|
|
|
Rocket is a web framework for Rust with a focus on ease of use, expressiveness,
|
|
and speed. Rocket makes it simple to write fast web applications without
|
|
sacrificing flexibility or type safety. All with minimal code.
|
|
|
|
> Rocket's so simple, you feel like you're doing something wrong. It's like if
|
|
> you're making fire with rocks and suddenly someone gives you a lighter. Even
|
|
> though you know the lighter makes fire, and does it even faster and better and
|
|
> with a simple flick, the rock's still in your brain.
|
|
>
|
|
> -- <cite>Artem "impowski" Biryukov, January 17, 2017, on **#rocket**</cite>
|
|
|
|
## New Features
|
|
|
|
Rocket v0.2 includes several new features that make developing Rocket
|
|
applications simpler, faster, and safer than ever before.
|
|
|
|
### Managed State
|
|
|
|
Undoubtedly, the star feature of this release is **managed state**. Managed
|
|
state allows you to pass state to Rocket prior to launching your application and
|
|
later retrieve that state from any request handler by simply including the
|
|
state's type in the function signature. It works in two easy steps:
|
|
|
|
1. Call `manage` on the `Rocket` instance corresponding to your application
|
|
with the initial value of the state.
|
|
2. Add a `State<T>` type to any request handler, where `T` is the type of the
|
|
value passed into `manage`.
|
|
|
|
Rocket takes care of the rest! `State` works through Rocket's [request
|
|
guards](@guide-v0.3/requests/#request-guards). You can call `manage` any number
|
|
of times, as long as each call corresponds to a value of a different type.
|
|
|
|
As a simple example, consider the following "hit counter" example application:
|
|
|
|
```rust
|
|
struct HitCount(AtomicUsize);
|
|
|
|
#[get("/")]
|
|
fn index(hit_count: State<HitCount>) -> &'static str {
|
|
hit_count.0.fetch_add(1, Ordering::Relaxed);
|
|
"Your visit has been recorded!"
|
|
}
|
|
|
|
#[get("/count")]
|
|
fn count(hit_count: State<HitCount>) -> String {
|
|
hit_count.0.load(Ordering::Relaxed).to_string()
|
|
}
|
|
|
|
fn main() {
|
|
rocket::ignite()
|
|
.mount("/", routes![index, count])
|
|
.manage(HitCount(AtomicUsize::new(0)))
|
|
.launch()
|
|
}
|
|
```
|
|
|
|
Visiting `/` will record a visit by incrementing the hit count by 1. Visiting
|
|
the `/count` path will display the current hit count.
|
|
|
|
One concern when using _managed state_ is that you might forget to call `manage`
|
|
with some state's value before launching your application. Not to worry: Rocket
|
|
has your back! Let's imagine for a second that we forgot to add the call to
|
|
`manage` on line 17 in the example above. Here's what the compiler would emit
|
|
when we compile our buggy application:
|
|
|
|
```rust
|
|
warning: HitCount is not currently being managed by Rocket
|
|
--> src/main.rs:4:21
|
|
|
|
|
4 | fn index(hit_count: State<HitCount>) -> &'static str {
|
|
| ^^^^^^^^^^^^^^^
|
|
|
|
|
= note: this State request guard will always fail
|
|
help: maybe add a call to 'manage' here?
|
|
--> src/main.rs:15:5
|
|
|
|
|
15| rocket::ignite()
|
|
| ^^^^^^^^^^^^^^^^
|
|
|
|
warning: HitCount is not currently being managed by Rocket
|
|
--> src/main.rs:10:21
|
|
|
|
|
10 | fn count(hit_count: State<HitCount>) -> String {
|
|
| ^^^^^^^^^^^^^^^
|
|
|
|
|
= note: this State request guard will always fail
|
|
help: maybe add a call to 'manage' here?
|
|
--> src/main.rs:15:5
|
|
|
|
|
15 | rocket::ignite()
|
|
| ^^^^^^^^^^^^^^^^
|
|
```
|
|
|
|
You can read more about managed state in the [guide](@guide-v0.3/state/), the
|
|
API docs for [manage](@api-v0.3/rocket/struct.Rocket.html#method.manage), and the API
|
|
docs for [State](@api-v0.3/rocket/struct.State.html).
|
|
|
|
### Unmounted Routes Lint
|
|
|
|
A common mistake that new Rocketeers make is forgetting to
|
|
[mount](@guide-v0.3/overview/#mounting) declared routes. In Rocket v0.2, Rocket
|
|
adds a _lint_ that results in a compile-time warning for unmounted routes. As a
|
|
simple illustration, consider the canonical "Hello, world!" Rocket application
|
|
below, and note that we've forgotten to mount the `hello` route:
|
|
|
|
```rust
|
|
#[get("/")]
|
|
fn hello() -> &'static str {
|
|
"Hello, world!"
|
|
}
|
|
|
|
fn main() {
|
|
rocket::ignite().launch();
|
|
}
|
|
```
|
|
|
|
When this program is compiled, the compiler emits the following warning:
|
|
|
|
```rust
|
|
warning: the 'hello' route is not mounted
|
|
--> src/main.rs:2:1
|
|
|
|
|
2 | fn hello() -> &'static str {
|
|
| _^ starting here...
|
|
3 | | "Hello, world!"
|
|
4 | | }
|
|
| |_^ ...ending here
|
|
|
|
|
= note: Rocket will not dispatch requests to unmounted routes.
|
|
help: maybe add a call to 'mount' here?
|
|
--> src/main.rs:7:5
|
|
|
|
|
7 | rocket::ignite().launch();
|
|
| ^^^^^^^^^^^^^^^^
|
|
```
|
|
|
|
The lint can be disabled selectively per route by adding an
|
|
`#[allow(unmounted_route)]` annotation to a given route declaration. It can also
|
|
be disabled globally by adding `#![allow(unmounted_route)]`. You can read more
|
|
about this lint in the [codegen documentation](@api-v0.3/rocket_codegen/index.html).
|
|
|
|
### Configuration via Environment Variables
|
|
|
|
A new feature that makes deploying Rocket apps to the cloud a little easier is
|
|
configuration via environment variables. Simply put, any configuration parameter
|
|
can be set via an environment variable of the form `ROCKET_{PARAM}`, where
|
|
`{PARAM}` is the name of the configuration parameter. For example, to set the
|
|
`port` Rocket listens on, simply set the `ROCKET_PORT` environment variable:
|
|
|
|
```sh
|
|
ROCKET_PORT=3000 cargo run --release
|
|
```
|
|
|
|
Configuration parameters set via environment variables take precedence over
|
|
parameters set via the `Rocket.toml` configuration file. Note that _any_
|
|
parameter can be set via an environment variable, include _extras_. For more
|
|
about configuration in Rocket, see the [configuration section of the
|
|
guide](@guide-v0.3/configuration).
|
|
|
|
### And Plenty More!
|
|
|
|
Rocket v0.2 is full of many new features! In addition to the three features
|
|
described above, v0.2 also includes the following:
|
|
|
|
* `Config` structures can be built via `ConfigBuilder`, which follows the
|
|
builder pattern.
|
|
* Logging can be enabled or disabled on custom configuration via a second
|
|
parameter to the `Rocket::custom` method.
|
|
* `name` and `value` methods were added to `Header` to retrieve the name and
|
|
value of a header.
|
|
* A new configuration parameter, `workers`, can be used to set the number of
|
|
threads Rocket uses.
|
|
* The address of the remote connection is available via `Request.remote()`.
|
|
Request preprocessing overrides remote IP with value from the `X-Real-IP`
|
|
header, if present.
|
|
* During testing, the remote address can be set via `MockRequest.remote()`.
|
|
* The `SocketAddr` request guard retrieves the remote address.
|
|
* A `UUID` type has been added to `contrib`.
|
|
* `rocket` and `rocket_codegen` will refuse to build with an incompatible
|
|
nightly version and emit nice error messages.
|
|
* Major performance and usability improvements were upstreamed to the `cookie`
|
|
crate, including the addition of a `CookieBuilder`.
|
|
* When a checkbox isn't present in a form, `bool` types in a `FromForm`
|
|
structure will parse as `false`.
|
|
* The `FormItems` iterator can be queried for a complete parse via `completed`
|
|
and `exhausted`.
|
|
* Routes for `OPTIONS` requests can be declared via the `options` decorator.
|
|
* Strings can be percent-encoded via `URI::percent_encode()`.
|
|
|
|
## Breaking Changes
|
|
|
|
This release includes several breaking changes. These changes are listed below
|
|
along with a short note about how to handle the breaking change in existing
|
|
applications.
|
|
|
|
* **`Rocket::custom` takes two parameters, the first being `Config` by
|
|
value.**
|
|
|
|
A call in v0.1 of the form `Rocket::custom(&config)` is now
|
|
`Rocket::custom(config, false)`.
|
|
|
|
* **Tera templates are named without their extension.**
|
|
|
|
A templated named `name.html.tera` is now simply `name`.
|
|
|
|
* **`JSON` `unwrap` method has been renamed to `into_inner`.**
|
|
|
|
A call to `.unwrap()` should be changed to `.into_inner()`.
|
|
|
|
* **The `map!` macro was removed in favor of the `json!` macro.**
|
|
|
|
A call of the form `map!{ "a" => b }` can be written as: `json!({ "a": b
|
|
})`.
|
|
|
|
* **The `hyper::SetCookie` header is no longer exported.**
|
|
|
|
Use the `Cookie` type as an `Into<Header>` type directly.
|
|
|
|
* **The `Content-Type` for `String` is now `text/plain`.**
|
|
|
|
Use `content::HTML<String>` for HTML-based `String` responses.
|
|
|
|
* **`Request.content_type()` returns an `Option<ContentType>`.**
|
|
|
|
Use `.unwrap_or(ContentType::Any)` to get the old behavior.
|
|
|
|
* **The `ContentType` request guard forwards when the request has no
|
|
`Content-Type` header.**
|
|
|
|
Use an `Option<ContentType>` and `.unwrap_or(ContentType::Any)` for the old
|
|
behavior.
|
|
|
|
* **A `Rocket` instance must be declared _before_ a `MockRequest`.**
|
|
|
|
Change the order of the `rocket::ignite()` and `MockRequest::new()` calls.
|
|
|
|
* **A route with `format` specified only matches requests with the same
|
|
format.**
|
|
|
|
Previously, a route with a `format` would match requests without a format
|
|
specified. There is no workaround to this change; simply specify formats
|
|
when required.
|
|
|
|
* **`FormItems` can no longer be constructed directly.**
|
|
|
|
Instead of constructing as `FormItems(string)`, construct as
|
|
`FormItems::from(string)`.
|
|
|
|
* **`from_from_string(&str)` in `FromForm` removed in favor of
|
|
`from_form_items(&mut FormItems)`.**
|
|
|
|
Most implementation should be using `FormItems` internally; simply use the
|
|
passed in `FormItems`. In other cases, the form string can be retrieved via
|
|
the `inner_str` method of `FormItems`.
|
|
|
|
* **`Config::{set, default_for}` are deprecated.**
|
|
|
|
Use the `set_{param}` methods instead of `set`, and `new` or `build` in
|
|
place of `default_for`.
|
|
|
|
* **Route paths must be absolute.**
|
|
|
|
Prepend a `/` to convert a relative path into an absolute one.
|
|
|
|
* **Route paths cannot contain empty segments.**
|
|
|
|
Remove any empty segments, including trailing ones, from a route path.
|
|
|
|
## Bug Fixes
|
|
|
|
Three bugs were fixed in this release:
|
|
|
|
* Handlebars partials were not properly registered
|
|
([#122](https://github.com/rwf2/Rocket/issues/122)).
|
|
* `Rocket::custom` did not set the custom configuration as the `active`
|
|
configuration.
|
|
* Route path segments with more than one dynamic parameter were erroneously
|
|
allowed.
|
|
|
|
## General Improvements
|
|
|
|
In addition to new features, Rocket saw the following smaller improvements:
|
|
|
|
* Rocket no longer overwrites a catcher's response status.
|
|
* The `port` `Config` type is now a proper `u16`.
|
|
* Clippy issues injected by codegen are resolved.
|
|
* Handlebars was updated to `0.25`.
|
|
* The `PartialEq` implementation of `Config` doesn't consider the path or
|
|
session key.
|
|
* Hyper dependency updated to `0.10`.
|
|
* The `Error` type for `JSON as FromData` has been exposed as `SerdeError`.
|
|
* SVG was added as a known Content-Type.
|
|
* Serde was updated to `0.9`.
|
|
* Form parse failure now results in a **422** error code.
|
|
* Tera has been updated to `0.7`.
|
|
* `pub(crate)` is used throughout to enforce visibility rules.
|
|
* Query parameters in routes (`/path?<param>`) are now logged.
|
|
* Routes with and without query parameters no longer _collide_.
|
|
|
|
Rocket v0.2 also includes all of the new features, bug fixes, and improvements
|
|
from versions 0.1.1 through 0.1.6. You can read more about these changes in the
|
|
[v0.1
|
|
CHANGELOG](https://github.com/rwf2/Rocket/blob/v0.1/CHANGELOG.md).
|
|
|
|
## What's next?
|
|
|
|
Work now begins on Rocket v0.3! The focus of the next major release will be on
|
|
security. In particular, three major security features are planned:
|
|
|
|
1. **Automatic CSRF protection across all payload-based requests
|
|
([#14](https://github.com/rwf2/Rocket/issues/14)).**
|
|
|
|
Rocket will automatically check the origin of requests made for HTTP `PUT`,
|
|
`POST`, `DELETE`, and `PATCH` requests, allowing only authorized requests to
|
|
be dispatched. This includes checking `POST`s from form submissions and any
|
|
requests made via JavaScript.
|
|
|
|
2. **Encryption and signing of session-based cookies
|
|
([#20](https://github.com/rwf2/Rocket/issues/20)).**
|
|
|
|
Built-in session support will encrypt and sign cookies using a user supplied
|
|
`session_key`. Encryption and signing will occur automatically for
|
|
session-based cookies.
|
|
|
|
3. **Explicit typing of raw HTTP data strings
|
|
([#43](https://github.com/rwf2/Rocket/issues/43)).**
|
|
|
|
A present, the standard `&str` type is used to represent raw HTTP data
|
|
strings. In the next release, a new type, `&RawStr`, will be used for this
|
|
purpose. This will make it clear when raw data is being handled. The type
|
|
will expose convenient methods such as `.url_decode()` and `.html_escape()`.
|
|
|
|
Work on Rocket v0.3 will also involve exploring built-in support for user
|
|
authentication and authorization as well as automatic parsing of multipart
|
|
forms.
|
|
|
|
## Contributors to v0.2
|
|
|
|
The following wonderful people helped make Rocket v0.2 happen:
|
|
|
|
<ul class="columns">
|
|
<li>Cliff H</li>
|
|
<li>Dru Sellers</li>
|
|
<li>Eijebong</li>
|
|
<li>Eric D. Reichert</li>
|
|
<li>Ernestas Poskus</li>
|
|
<li>FliegendeWurst</li>
|
|
<li>Garrett Squire</li>
|
|
<li>Giovanni Capuano</li>
|
|
<li>Greg Edwards</li>
|
|
<li>Joel Roller</li>
|
|
<li>Josh Holmer</li>
|
|
<li>Liigo Zhuang</li>
|
|
<li>Lori Holden</li>
|
|
<li>Marcus Ball</li>
|
|
<li>Matt McCoy</li>
|
|
<li>Reilly Tucker Siemens</li>
|
|
<li>Robert Balicki</li>
|
|
<li>Sean Griffin</li>
|
|
<li>Seth Lopez</li>
|
|
<li>tborsa</li>
|
|
</ul>
|
|
|
|
Thank you all! Your contributions are greatly appreciated!
|
|
|
|
Looking to help with Rocket's development? Head over to [Rocket's
|
|
GitHub](https://github.com/rwf2/Rocket#contributing) and start
|
|
contributing!
|
|
|
|
## Start using Rocket today!
|
|
|
|
Not already using Rocket? Rocket is extensively documented, making it easy for
|
|
you to start writing your web applications in Rocket! See the
|
|
[overview](../../overview) or start writing code immediately by reading through
|
|
[the guide](@guide-v0.3).
|