Rocket/docs/overview.md

8.2 KiB

Overview

A quick glance at what makes Rocket special.

Introduction

This overview is a concise introduction to Rocket. There's also a full, detailed guide. If you want to get started immediately, see quickstart or the getting started guide. Otherwise, welcome!

Rocket makes writing web applications easy, fast, and fun. Rather than just talk about how, we'd rather show you. Below is a complete Rocket application. In fact, it's one of many complete, runnable examples in Rocket's GitHub. Try to see if you can figure it what it does.

#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;
use rocket::Rocket;

#[get("/<name>/<age>")]
fn hello(name: &str, age: u8) -> String {
    format!("Hello, {} year old named {}!", age, name)
}

fn main() {
    let mut rocket = Rocket::ignite();
    rocket.mount("/hello", routes![hello]);
    rocket.launch()
}

If you were to run this application, your console would show:

🛰  Mounting '/hello':
    => GET /hello/<name>/<age>
🚀  Rocket has launched from localhost:8000...

Here's a quick summary: this Rocket applications declares the hello route to GET /<name>/<age>, which returns a String formatted with name and age from the dynamic path, on lines 7 - 10. Then, in the main function, it creates a new Rocket instance, mounts the hello route at "/hello", and launches the application. That's it! Let's break it down.

Let's start at the beginning: lines 1 and 2. Rocket depends on Rust nightly; it makes extensive use of Rust's code generation facilities through compiler plugins. Plugins are still experimental, so we have to tell Rust that we're okay with that by writing #![feature(plugin)]. We also have to tell the compiler to use Rocket's code generation crate during compilation with #![plugin(rocket_codegen)].

Routes

Lines 4 and 5 bring rocket::Rocket into the namespace. The fun begins on line 7, where the hello route is declared. Let's talk about routes for a bit.

Every Rocket application is composed of some number of routes. A route is a combination of:

  • A set of parameters to match an incoming request against.
  • A request handler to process the request and return a response.

The set of parameters to match against includes static paths, dynamic paths, path segments, forms, query strings, and format specifiers. Rocket uses Rust attributes, which look like function decorators in other languages, to make declaring routes easy. In Rocket, you declare a route by annotating a function with the set of parameters to match against. A complete route declaration looks like:

#[get("/path/to/match/against")]

You can use put, post, delete, and patch in place of get.

Dynamic Paths

The hello route declaration on line 7 tells Rocket that the hello function will handle HTTP GET requests to the <name>/<age> path. The handler uses name and age from the path to format and return a String to the user. Here are lines 7 - 10 again:

#[get("/<name>/<age>")]
fn hello(name: &str, age: u8) -> String {
    format!("Hello, {} year old named {}!", age, name)
}

The <name> and <age> parts of the path are dynamic: the actual values for these segments won't be known until someone visits a URL that matches. For example, if someone visit Mike/21, <name> will be "Mike", and <age> will be 21. If someone else visits Bob/91, <name> and <age> will be "Bob" and 91, respectively. Rocket automatically parses dynamic path segments and passes them your request handler. You can immediately use the name and age in the handler - no parsing, no checking.

But wait: what happens if someone goes to a URL with an <age> that isn't a valid u8? In that case, Rocket doesn't call the handler. Instead, it tries other matching routes and ultimately returns a 404 if all of them fail. If you want to know if the user passed in a bad <age>, use a Result<u8, &str> or an Option<u8> instead. Rocket has mechanisms to handle route collisions, where multiple routes can match the same URLs, too. For more details on routing, see the routing chapter of the guide.

Oh, one more thing before we move on! Notice how dynamic path parameters can be of different types? Actually, path parameters can be of any type, as long as that type implements Rocket's FromParam trait. Rocket uses the FromParam implementation to parse and validate the parameter for you automatically. We've implemented FromParam for every reasonable type in the standard library. See the FromParam documentation for more.

Mounting

Now that we're set with the hello route, let's move on to lines 13 - 14. Before Rocket dispatches requests to a route, the route needs to be mounted. And before we can mount a route, we need an instance of Rocket.

Mounting a route is like namespacing it. Routes can be mounted any number of times. Mounting happens with the mount method on a Rocket instance, which itself is created with the ignite() static method. The mount method takes a list of route handlers given inside of the route! macro. The route! macro ties Rocket's code generation to your application.

Let's look at lines 13 - 14 again, which we reproduce below:

let mut rocket = Rocket::ignite();
rocket.mount(“/hello”, routes![hello]);

Line 13 creates the new Rocket instance, and line 14 mounts the hello route at the "/hello" path. This makes the hello handler available at /hello/<name>/<age>. Notice how the mounting path is prepended to the route's path. There's a ton more information about mounting in the guide.

Launching

Now that the route is declared and mounted, the application is ready to launch! To launch an application and have Rocket start listening for and dispatching requests, simply call launch on the Rocket instance where routes are mounted. This happens on line 14. Here it is again:

rocket.launch()

Again, running our full example will show the following in the console:

🛰  Mounting '/hello':
    => GET /hello/<name>/<age>
🚀  Rocket has launched from localhost:8000...

If you visit http://localhost:8000/hello/Mike/21, you'll see "Hello, 21 year old named Mike!". If you have the example running, try visiting other valid and invalid paths and see what happens! This example's complete crate, ready to cargo run, can be found at Github.

Requests

There's a lot more to do with requests. Let's take a closer look.

Forms and Queries

Handling forms and query parameters couldn't be easier: declare a form or query parameter in the route attribute and handler, then ensure that its type implements (the automatically derivable) FromForm.

Form parameters are declared by adding form = "<param_name>" to the route attribute. Say your application is processing a form submission for a new todo Task. The form contains two fields: complete, a checkbox, and description, a text field. You can easily handle the form request in Rocket as follows:

#[derive(FromForm)]
struct Task {
    description: String,
    complete: bool
}

#[post("/todo", form = "<task>")]
fn new(task: Task) -> String {
    ...
}

If you change your mind and want to use query strings for the form instead, simple declare <task> as a query parameter as follows:

#[get("/todo?<task>")]
fn new(task: Task) -> String {
    ...
}

If the form request is invalid according to the form's type, the handler doesn't get called. Just like in path parameters, you can use Option or Result in form structure fields to be notified of parsing errors. You can also easily define your own types to validate forms and queries against. For more details, see the forms and queries chapters of the guide.

Guards

In addition to FromParam types, you can include any number of types that implement the FromRequest trait in handler arguments. For example, to retrieve cookies from a request, you can use a parameter of &Cookie type in a request handler:

#[get("/hello")]
fn hello(cookies: &Cookies) -> ..

JSON

Responses

Responder

Templates

JSON

What's next?