Rocket of Rust

Introduction

Rocket’s design is centered around three core philosophies:

  • Function declaration and parameter type should contain all necessary information to validate and process a request. This immediately prohibits APIs where request state is retrieved from a global context. As a result, request handling is self-contained in Rocket: handlers are regular functions with regular arguments.

  • All request handling information should be typed. Because the web and HTTP are themselves untyped(or stringly typed, as some call it), this means that something or someone has to convert strings to native types. Rocket does this for you with zero programming overhead.

  • Decisions should not be forced. Templates, serialization, sessions and just about everything else are all pluggable, optional components. While Rocket has official support and libraries for each of these, they are completely optional and swappable.

Getting Started

1
cargo new hello-rocket --bin

Add Dependencies

1
2
3
[dependencies]
rocket = "0.3.3"
rocket_codegen = "0.3.3"
1
2
3
4
5
6
7
8
9
10
11
12
13
#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;

#[get("/")]
fn index() -> &'static str {
"Hello World"
}

fn main () {
rocket::ignite().mount("/", routes![index]).launch();
}

It creates an index route, mount the route at the / path, and launches the application. Compile and run the program with cargo run, you should see the following:

1
2
3
4
5
6
7
8
9
10
11
🔧  Configured for development.
=> address: localhost
=> port: 8000
=> log: normal
=> workers: [core count * 2]
=> secret key: generated
=> limits: forms = 32KiB
=> tls: disabled
🛰 Mounting '/':
=> GET /
🚀 Rocket has launched from http://localhost:8000

Overview

Rocket provides primitives to build web servers and applications with Rust: the rest is up to you. In short, Rocket provides routing, pre-processing of requests and psot-processing of response. Your application code instructs Rocket what to pre-process and post-process and fills the gaps between pre-processing and post-processing.

LifeCycle

Rocket’s main task is to listen for incoming web prequests, dispatch the request to the application code, and retur a response to the client. We call the process that goes form request to response the ‘lifecycle’. The lifecycle can be summarized as following sequence of steps:

  • Routing

    Rocket parse an incoming HTTP request into native structure that your code operates on indirectly. Rocket determines which request handler to invoke by matching against route attributes declared in your application.

  • Validation

    Rocket validates the incoming request against types and guards present in the matched route. If validation fails, Rocket forwards the request to the next matching route or calls an error handler.

  • Processing

    The request handler associated with the route is invoked with validated arguments. This is the main business logic of an application. Processing completes by returning a Response.

  • Response

    The returned Response is processed. Rocket generates the appropriate HTTP response and sends it to the client. This completes the lifecycle. Rocket continues listening for requesting, restarting the lifecycle for each incoming request.

Routing

Rocket applications are centered around routes and handlers. A route is a combination of:

  • A set of parameters to match an incoming request against.

  • A handler to process the request and return a response.

A handler is simply a function that takes an arbirary number of arguments and returns any arbitrary type.

The parameters to match against include static paths, dynamic paths, path segments, forms, query string, request format specifiers and body data. Rocket uses attributes, which look like function decoration in other languages to make declaring routes easy. Routes are declared by annotating a function, the handler, with a set of parameters to match against. A complete route declaration looks like this:

1
2
3
4
#[get("/world")]
fn world() -> &'static str {
"Hello World"
}

This declare the world route to match against the static path /world on incoming GET requests. The world route is simple, but additional route parameters are necessary when building more interesting application.

Mounting

Before Rocket can dispatch requests to a route, the route needs to be mounted. Mounting a route is like namespacing it. Routes are mounted via the mount method on a Rocket instance. A Rocket instance is typically created with the racket::ignite() static method.

The mount method takes:

  • A path to namespace a list of routes under.

  • A list of route handlers through the routes! macro, typing Rocket’s code generation to your application.

For instance, to mount the world route we declared above, we can write the following:

1
rocket::ignite().mount("/hello", routes![world]);

This create a new Rocket instance via the ignite function and mounts the world route to the /hello path. As a result, GET requests to the /hello/world path will be directed to the world function.

Namespacing

When a route is declared inside a module other than the root, you may find yourself with unexpected errors when mounting:

1
2
3
4
5
6
7
8
9
mod other {
#[get("/world")]
pub fn world() -> String {
"Hello World"
}
}
fn main () {
rocket::ignite().mount("/hello", routes![world]);
}

This occurs because the routes! macro implicitly converts the route’s name into the name of a structure generated by Rocket’s code generation. The solution is to name the route by a module path instead:

1
rocket::ignite().mount("/hello", routes![other::world]);

Launching

Now that Rocket knows about the route, you can tell Rocket to start accepting requests via the launch method. The method starts up the server and wait for incoming requests. When a request arrives, Rocket finds the matching route and dispatches the requests to the route’s handler.

We typically call launch from the main function. Our complete Hello World application thus looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;

#[get("/world")]
fn world() -> String {
"Hello World"
}

fn main () {
rocket::ignite().mount("/hello", routes![world]).launch();
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×