Rocket/site/news/2017-02-06-version-0.2.md

397 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/SergioBenitez/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, expressibility,
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/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/state/), the API
docs for
[manage](https://api.rocket.rs/rocket/struct.Rocket.html#method.manage), and the
API docs for [State](https://api.rocket.rs/rocket/struct.State.html).
### Unmounted Routes Lint
A common mistake that new Rocketeers make is forgetting to
[mount](/guide/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](https://api.rocket.rs/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/overview/#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/SergioBenitez/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/SergioBenitez/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/SergioBenitez/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/SergioBenitez/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/SergioBenitez/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/SergioBenitez/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).