Rocket v0.4: Typed URIs, Database Support, Revamped Queries, & More!#

Posted by Sergio Benitez on December 08, 2018

I am elated to announce that the next major release of Rocket is now available! Rocket 0.4 is a step forward in every direction: it is packed with features and improvements that increase developer productivity, improve application security and robustness, provide new opportunities for extensibility, and deliver a renewed degree of toolchain stability.

Rocket 0.4 is the culmination of more than a year of development. During this time, more than 600 changes were committed, almost 400 issues were closed, and over 165 pull requests were submitted. The Rocket community has proved steadfast in its support: a sincere thank you to everyone involved!

About Rocket#

Rocket is a web framework for Rust with a focus on usability, security, and performance. Rocket makes it simple to write fast, secure web applications without sacrificing flexibility or type safety.

Not already using Rocket? Join the tens of thousands of users and hundreds of companies happily using Rocket today! Rocket's extensive documentation makes it easy. Get started now by reading through the guide or learning more from the overview.

Rocket 0.4 is the largest release to date by a wide margin. It is packed with hundreds of changes. We highlight the largest of them here. For a complete description of everything new and different in 0.4, please see the CHANGELOG.

Maintainers += 1#

An open source project is as much about the people as it is about the code. This is why I am delighted to welcome @jebrosen as Rocket's first co-maintainer! Jeb is directly responsible for several of the new features in 0.4, has painstakingly code reviewed many other changes, and actively answers questions and resolves issues on GitHub, IRC, and offline.

Needless to say, Rocket is a better project thanks to you, Jeb. Welcome!

Codegen Rewrite#

In 0.4, the rocket_codegen crate has been entirely rewritten to use to-be-stable procedural macro APIs where it previously used private, unstable rustc APIs. While this is largely an internal change, it has big, positive implications for all Rocket users.

First and foremost, the path to Rocket on stable is now clearly in sight. While there are still hurdles to overcome, we are actively working with the Rust team to make Rocket on stable a reality as soon as possible. We expect the next major Rocket release to support the stable channel.

Second, but equally important, we expect breakages due to nightly changes to drop dramatically, likely to zero. This means that Rocket is largely already de-facto toolchain stable.

The new prelude import for Rocket applications is:

1 2 3 4 5 6 - #![feature(plugin)] - #![plugin(rocket_codegen)] + #![feature(proc_macro_hygiene, decl_macro)] - extern crate rocket; + #[macro_use] extern crate rocket;

rocket_codegen should not be a direct dependency. Remove it from your Cargo.toml :

1 2 3 4 [dependencies] - rocket = "0.3" + rocket = "0.4" - rocket_codegen = "0.3"

Typed URIs#

Rocket 0.4 introduces the uri! macro, allowing you to construct URIs to routes in a robust, type-safe, and URI-safe manner. Type or route parameter mismatches are caught at compile-time, and changes to route URIs are automatically reflected in the generated URIs.

To illustrate, consider the following route:

1 2 #[get( "/person/<name>?<age>" )] fn person ( name : String , age : Option < u8 > )

URIs to this person route can be created as follows:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // with unnamed parameters, in route URI declaration order let uri = uri ! ( person : "Mike Smith" , 28 ); assert_eq ! ( uri . to_string (), "/person/Mike%20Smith?age=28" ); // with named parameters, order irrelevant let uri = uri ! ( person : name = "Mike" , age = 28 ); let uri = uri ! ( person : age = 28 , name = "Mike" ); assert_eq ! ( uri . to_string (), "/person/Mike?age=28" ); // with a specific mount-point let uri = uri ! ( "/api" , person : name = "Mike" , age = 28 ); assert_eq ! ( uri . to_string (), "/api/person/Mike?age=28" ); // with optional query parameters ignored let uri = uri ! ( person : "Mike" , _ ); let uri = uri ! ( person : name = "Mike" , age = _ ); assert_eq ! ( uri . to_string (), "/person/Mike" );

Should your route's URI change in an incompatible manner, or should you mistype parameters, Rocket informs you of the error at compile-time with a helpful message:

1 2 3 4 5 6 7 error : person route uri expects 2 parameters but 1 was supplied - -> examples / uri / src / main . rs : 9 : 29 | 9 | uri ! ( person : "Mike Smith" ); | ^^^^^^^^^^^^ | = note : expected parameters : name : String , age : Option < u8 >

The same applies to type errors: Rocket informs you of any type errors at compile-time as well:

1 2 3 4 5 6 error : the trait bound u8 : FromUriParam < Query , & str > is not satisfied - -> examples / uri / src / main . rs : 9 : 35 | 9 | uri ! ( person : age = "10" , name = "Mike" ); | ^^^^ FromUriParam < Query , & str > is not implemented for u8 |

We recommend that uri! is exclusively used when constructing route URIs. For more information on typed URIs, see the new Typed URIs guide section and the uri! macro documentation.

Database Support#

Rocket now includes built-in, ORM-agnostic support for database connection pooling. More specifically, Rocket allows you to easily configure and connect your Rocket application to databases through connection pools in three simple, largely automated steps:

Configure databases in Rocket.toml . Associate a request guard type and fairing with each database. Use the request guard to retrieve a connection in a handler.

As an example, for a Diesel-based SQLite database named sqlite_logs , your Rocket.toml would record the URL to the database in the databases table:

1 2 [global.databases] sqlite_logs = { url = "/path/to/database.sqlite" }

In the application, a unit-like struct with one internal type (the database connection) is decorated with the #[database] attribute and the name of the configured database. This generates a fairing which must then be attached:

1 2 3 4 #[database( "sqlite_logs" )] struct LogsDbConn ( diesel :: SqliteConnection ); rocket :: ignite (). attach ( LogsDbConn :: fairing ())

That's it! Whenever a connection to the database is needed, the type can be used as a request guard:

1 2 3 4 #[get( "/logs/<id>" )] fn get_logs ( conn : LogsDbConn , id : usize ) -> Result < Logs > { logs :: filter ( id . eq ( log_id )). load ( & conn ) }

For more information on Rocket's database support, see the new Database guide section and the rocket_contrib::databases module documentation.

Revamped Queries#

In Rocket 0.4, query string handling has been completely overhauled, resolving some of the most called for requests in Rocket's history (#608). The new query handling route syntax and semantics were designed with the following goals in mind:

Enable matching of static query components.

No special-casing of any kind, preferring type-driven flows.

Ad-hoc matching of specific query key/value pairs.

Lenient parsing by default, allowing missing parameters.

Order-independent matching of query parameters.

To illustrate the new system in action, consider the following route:

1 2 3 4 5 6 7 8 9 #[derive(FromForm)] struct DogDetails { color : Color , height : Inches , weight : Pounds } #[get( "/animal?dog&<name>&<nickname>&<rest..>" )] fn dog ( name : String , nickname : Option < String > , rest : Form < DogDetails > )

This route matches any GET request with a path of /animal , a static query component of dog , and key/value parameters of color , height , and weight that validate as Color , Inches , and Pounds , respectively. Furthermore, it optionally accepts a key/value parameter of nickname . If the value is present, nickname will be Some ; if it is not, nickname will be None .

Single parameters ( <param> ) like name and nickname are validated using the existing FromFormValue trait while trailing parameters ( <param..> ) are validated using the new FromQuery trait. Both traits are user implementable, and FromFormValue can be derived.

For more details on handling query strings, see the new Query Strings guide section and the updated route attribute documentation.

Stateful Handlers#

The type of a handler has been generalized in 0.4 to any type that implements the new Handler trait. Among other things, this allows handlers to refer to internal state during request handling.

The new StaticFiles contrib type uses this functionality to provide easier-than-ever static file serving. For example, to make local files from a /static directory accessible at /public , you need simply write:

1 2 3 4 5 fn main () { rocket :: ignite () . mount ( "/public" , StaticFiles :: from ( "/static" )) . launch (); }

We encourage users to explore the new Handler API and contribute libraries with pluggable handlers! For more details, see the Handler documentation.

Responder Derive#

In Rocket 0.4, the Responder trait can be derived for enum s and struct s with named fields. This greatly simplifies returning multiple types of responses from a single handler.

To illustrate, consider a route that returns either a Json<Info> structure for 401 (unauthorized) errors or a NamedFile with a dynamic Content-Type for 404 (not found) errors. To accomplish this previously, Result values could be arbitrarily nested, an unappealing and semantically incorrect approach. Alternatively, an enum could be declared with the appropriate variants, and Responder could be manually implemented for the enum . As of 0.4, that implementation can be automatically derived:

1 2 3 4 5 6 7 #[derive(Responder, Debug)] enum Error { #[response(status = 401)] Unauthorized ( Json < Info > ), #[response(status = 404)] NotFound ( NamedFile , ContentType ), }

A value of this type can then be returned from a hander or used as part of wrapping responders:

1 2 3 4 5 6 7 8 9 10 #[get( "/<item>" )] fn handler ( user : Option < User > , item : Option < Item > ) -> Result < T , Error > { if user . is_none () { Err ( Error :: Unauthorized (..)) } else if item . is_none () { Err ( Error :: NotFound (..)) } else { Ok (..) } }

The status for each variant will be automatically set to the value of the status variant attribute, and fields beyond the first will be added as headers to the response (here, ContentType ).

For more on using the Responder derive, see the new Custom Responders guide section and the Responder derive documentation.

Live Template Reloading#

Rocket 0.4 automatically reloads changed templates at runtime without requiring recompilation. This works on all major platforms. For security and performance reasons, live template reloading is only enabled when the application is compiled in debug mode.

There is no configuration necessary: this just works out of the box!

And Plenty More!#

In addition to the features highlighted above, Rocket 0.4 also contains the following new features:

Introduced Request-Local State.

Introduced transforming data guards via FromData::transform() .

. Introduced the SpaceHelmet security and privacy headers fairing.

security and privacy headers fairing. Private cookies are gated behind a private-cookies default feature.

default feature. Added derive for FromFormValue .

. Added Template::custom() for customizing templating engines.

for customizing templating engines. Cookies are automatically tracked and propagated by Client .

. Private cookies can be added to local requests with LocalRequest::private_cookie() .

. Release builds default to the production environment.

environment. Keep-alive can be configured via the keep_alive configuration parameter.

configuration parameter. Allow CLI colors and emoji to be disabled with ROCKET_CLI_COLORS=off .

. Route format accepts shorthands such as json and html .

accepts shorthands such as and . Implemented Responder for Status .

for . Added Response::cookies() for retrieving response cookies.

for retrieving response cookies. All logging is disabled when log is set to off .

is set to . Added Metadata guard for retrieving templating information.

guard for retrieving templating information. The Uri type parses according to RFC 7230 into one of Origin , Absolute , or Authority .

type parses according to RFC 7230 into one of , , or . Added Outcome::and_then() , Outcome::failure_then() , and Outcome::forward_then() .

, , and . Implemented Responder for &[u8] .

for . Any T: Into<Vec<Route>> can be mount() ed.

can be ed. Added Request::get_query_value() for retrieving a query value by key.

for retrieving a query value by key. Applications can launch without a working directory.

Added State::from() for constructing State values.

Breaking Changes#

This release includes many breaking changes. Please see the CHANGELOG for a complete list of breaking changes along with details on handling the breaking change in existing applications.

Rocket 0.3 will continue as a security maintance release only. All users are encouraged to migrate their applications to 0.4.

General Improvements#

In addition to new features, Rocket saw the following improvements:

Log messages now refer to routes by name.

Collision errors on launch name the colliding routes.

Launch fairing failures refer to the failing fairing by name.

The default 403 catcher now references authorization, not authentication.

catcher now references authorization, not authentication. Private cookies are set to HttpOnly and are given an expiration date of 1 week by default.

and are given an expiration date of 1 week by default. A Tera templates example was added.

All macros, derives, and attributes are individually documented in rocket_codegen .

. Invalid client requests receive a response of 400 instead of 500 .

instead of . Response bodies are reliably stripped on HEAD requests.

requests. Added a default catcher for 504: Gateway Timeout .

. Configuration information is logged in all environments.

Use of unsafe was reduced from 9 to 2 in core library.

was reduced from 9 to 2 in core library. FormItems now parses empty keys and values as well as keys without values.

now parses empty keys and values as well as keys without values. Added Config::active() as a shorthand for Config::new(Environment::active()?) .

as a shorthand for . Address/port binding errors at launch are detected and explicitly emitted.

Flash cookies are cleared only after they are inspected.

cookies are cleared only after they are inspected. Sync bound on AdHoc::on_attach() , AdHoc::on_launch() was removed.

bound on , was removed. AdHoc::on_attach() , AdHoc::on_launch() accept an FnOnce .

, accept an . Added Config::root_relative() for retrieving paths relative to the configuration file.

for retrieving paths relative to the configuration file. Added Config::tls_enabled() for determining whether TLS is actively enabled.

for determining whether TLS is actively enabled. ASCII color codes are not emitted on versions of Windows that do not support them.

Added FLAC ( audio/flac ), Icon ( image/x-icon ), WEBA ( audio/webm ), TIFF ( image/tiff ), AAC ( audio/aac ), Calendar ( text/calendar ), MPEG ( video/mpeg ), TAR ( application/x-tar ), GZIP ( application/gzip ), MOV ( video/quicktime ), MP4 ( video/mp4 ), ZIP ( application/zip ) as known media types.

), Icon ( ), WEBA ( ), TIFF ( ), AAC ( ), Calendar ( ), MPEG ( ), TAR ( ), GZIP ( ), MOV ( ), MP4 ( ), ZIP ( ) as known media types. Added .weba ( WEBA ), .ogv ( OGG ), .mp4 ( MP4 ), .mpeg4 ( MP4 ), .aac ( AAC ), .ics ( Calendar ), .bin ( Binary ), .mpg ( MPEG ), .mpeg ( MPEG ), .tar ( TAR ), .gz ( GZIP ), .tif ( TIFF ), .tiff ( TIFF ), .mov ( MOV ) as known extensions.

( ), ( ), ( ), ( ), ( ), ( ), ( ), ( ), ( ), ( ), ( ), ( ), ( ), ( ) as known extensions. Interaction between route attributes and declarative macros has been improved.

Generated code now logs through logging infrastructures as opposed to using println! .

. Routing has been optimized by caching routing metadata.

Form and LenientForm can be publicly constructed.

and can be publicly constructed. Console coloring uses default terminal colors instead of white.

Console coloring is consistent across all messages.

i128 and u128 now implement FromParam , FromFormValue .

and now implement , . The base64 dependency was updated to 0.10 .

dependency was updated to . The log dependency was updated to 0.4 .

dependency was updated to . The handlebars dependency was updated to 1.0 .

dependency was updated to . The tera dependency was updated to 0.11 .

dependency was updated to . The uuid dependency was updated to 0.7 .

dependency was updated to . The rustls dependency was updated to 0.14 .

dependency was updated to . The cookie dependency was updated to 0.11 .

Rocket v0.5 is scheduled to be at least as exciting as 0.4! As always, the focus continues to be usability, stability, security, and performance. With this in mind, the roadmap for 0.5 includes:

Support for Rust Stable (#19) Finally! Rocket 0.5 will compile and run on stable versions of the Rust compiler. Asynchronous Request Handling (#17) In 0.5, Rocket will migrate to the latest asynchronous version of hyper and futures with compatibility for async / await syntax. Of utmost importance is preserving Rocket's usability. As such, these changes will be largely internal, with asynchronous I/O peeking over the covers only when explicitly desired or required. As a side effect, we expect a substantial performance boost from the migration as well as resolution to long-standing issues. Multipart Form Support (#106) The lack of built-in multipart form support makes handling file uploads and other submissions much more cumbersome than necessary. Rocket 0.5 will generalize its existing forms infrastructure to handle multipart forms. Stronger CSRF and XSS Protection (#14) Since 0.3, Rocket uses SameSite: Strict private cookies to prevent CSRF attacks. This technique is only tenable in newer browsers. In 0.5, Rocket will protect against CSRF using more robust techniques. Rocket will also add support for automatic, browser-based XSS protection.

Rocket v0.4 Contributors#

The following wonderful people helped make Rocket 0.4 happen:

Alexander Mielczarek

Alex Bowers

Alfie John

Alva Snædís

Ashley Williams

Beatriz Rizental

bohov

Christophe Courtaut

David Darrell

Desmond

Divyahans Gupta

Donald Robertson

EloD10

Eric Dattore

Henning Kowalk

Imron Alston

Jeb Rosen

kryptan

Kyle Clemens

lerina

Linus Unnebäck

Lukas Abfalterer

Marc Mettke

Max Furman

messense

Ning Sun

Philip Jenvey

Pyry Kontio

Richo Healey

Riley Trautman

Rolf Schmidt

Rukai

Sean Stangl

Sébastien Santoro

Sergio Benitez

Stanislav Ivanov

Tal Garfinkel

Tobias Stolzmann

Ville Hakulinen

Vishal Sodani

Zack Chandler

Zac Pullar-Strecker

Thank you all! Your contributions are greatly appreciated!

Looking to help with Rocket's development? Head over to Rocket's GitHub and start contributing!