mirror of https://github.com/rwf2/Rocket.git
A somewhat complete overview.
This commit is contained in:
parent
81f45608a1
commit
79eab0e907
|
@ -0,0 +1,245 @@
|
||||||
|
# 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](guide). If you want to get started immediately, see
|
||||||
|
[quickstart](guide/quickstart) or the [getting started
|
||||||
|
guide](guide/getting_started). Otherwise, welcome!
|
||||||
|
|
||||||
|
Rocket makes writing web applications easy, fast, and fun. Below is a complete
|
||||||
|
Rocket application. In fact, it's [one of many](thisexample) complete, runnable
|
||||||
|
examples in [Rocket's git repository](github). Can you figure out what it does?
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#![feature(plugin)]
|
||||||
|
#![plugin(rocket_codegen)]
|
||||||
|
|
||||||
|
extern crate rocket;
|
||||||
|
|
||||||
|
#[get("/<name>/<age>")]
|
||||||
|
fn hello(name: &str, age: u8) -> String {
|
||||||
|
format!("Hello, {} year old named {}!", age, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
rocket::ignite()
|
||||||
|
.mount("/hello", routes![hello])
|
||||||
|
.launch();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you were to run this application, your console would show:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
🔧 Configured for development.
|
||||||
|
=> listening: localhost:8000
|
||||||
|
=> logging: Normal
|
||||||
|
=> session key: false
|
||||||
|
🛰 Mounting '/hello':
|
||||||
|
=> GET /hello/<name>/<age>
|
||||||
|
🚀 Rocket has launched from localhost:8000...
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's a quick summary of what it does: first, on lines 7 - 10, it declares the
|
||||||
|
`hello` route to `GET /<name>/<age>`, which returns a `String` formatted with
|
||||||
|
`name` and `age` from the dynamic path. 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 this down.
|
||||||
|
|
||||||
|
We'll start with lines 1 and 2. Rocket depends on the latest version 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)]`. Lines 4 and 5 bring `rocket::Rocket` into the
|
||||||
|
namespace.
|
||||||
|
|
||||||
|
# Routes
|
||||||
|
|
||||||
|
The fun begins on line 7, where the `hello` route and request handler are
|
||||||
|
declared.
|
||||||
|
|
||||||
|
Rocket applications are composed primarily of request handlers and routes. A
|
||||||
|
_request handler_ is a function that takes an arbitrary number of arguments and
|
||||||
|
returns a response. 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 request format specifiers. Rocket uses
|
||||||
|
Rust attributes, which look like function decorators in other languages, to make
|
||||||
|
declaring routes easy. Routes are declares by annotating a function with the set
|
||||||
|
of parameters to match against. A complete route declaration looks like:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/index")]
|
||||||
|
fn index() -> &str { "Hello, World!" }
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use `put`, `post`, `delete`, and `patch` in place of `get`.
|
||||||
|
|
||||||
|
## Dynamic Paths
|
||||||
|
|
||||||
|
The `hello` route declaration beginning on line 7 of our example applications
|
||||||
|
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:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[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 matching URL. 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 to the request handler in variables with matching names. This
|
||||||
|
means that `name` and `age` can be used immediately 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
|
||||||
|
_forwards_ the request to the next matching route, if any, and ultimately
|
||||||
|
returns a `404` if all of them fail. If you want to know if the user passed in a
|
||||||
|
bad `<age>`, simply use a `Result<u8, &str>` or an `Option<u8>` type for `age`
|
||||||
|
instead. For more details on routing, route collisions, and much more see the
|
||||||
|
[routing](guide/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 plenty of types in the standard library. See the
|
||||||
|
[FromParam](docs) documentation for more.
|
||||||
|
|
||||||
|
## Mounting
|
||||||
|
|
||||||
|
Now that we understand 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. If you'd like to learn more
|
||||||
|
about the `route!` macro, see the [internals guide](guide/internals).
|
||||||
|
|
||||||
|
Let's look at lines 13 - 14 again, which we reproduce below:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
rocket::ignite()
|
||||||
|
.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](/guides/mounting).
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
🛰 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](https://github.com/SergioBenitez/Rocket/tree/master/examples/hello_world).
|
||||||
|
|
||||||
|
# Requests
|
||||||
|
|
||||||
|
There's a lot more we can do with requests. The [requests](guide/requests)
|
||||||
|
chapter of the guide talks about requests in details. We'll give you a short
|
||||||
|
overview of some of the more important and useful features here.
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[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:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[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](guide/forms) and [queries](guide/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:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/hello")]
|
||||||
|
fn hello(cookies: &Cookies) -> ..
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
|
||||||
|
# Responses
|
||||||
|
|
||||||
|
## Responder
|
||||||
|
|
||||||
|
## Templates
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
|
||||||
|
# What's next?
|
||||||
|
|
522
docs/overview.md
522
docs/overview.md
|
@ -9,9 +9,69 @@ detailed guide](guide). If you want to get started immediately, see
|
||||||
[quickstart](guide/quickstart) or the [getting started
|
[quickstart](guide/quickstart) or the [getting started
|
||||||
guide](guide/getting_started). Otherwise, welcome!
|
guide](guide/getting_started). Otherwise, welcome!
|
||||||
|
|
||||||
Rocket makes writing web applications easy, fast, and fun. Below is a complete
|
Rocket makes writing web applications easy, fast, and fun. It is Rocket's goal
|
||||||
Rocket application. In fact, it's [one of many](thisexample) complete, runnable
|
to have you write as little code as necessary to accomplish your goal. In
|
||||||
examples in [Rocket's git repository](github). Can you figure out what it does?
|
practice, this means that your code will be free of boilerplate and that the
|
||||||
|
common tasks will be handled for you.
|
||||||
|
|
||||||
|
# Routes and Handlers
|
||||||
|
|
||||||
|
Rocket applications are centered around routes and handlers.
|
||||||
|
|
||||||
|
A _handler_ is simply a function that takes an arbitrary number of arguments and
|
||||||
|
returns a response. 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.
|
||||||
|
|
||||||
|
The set of parameters to match against includes static paths, dynamic paths,
|
||||||
|
path segments, forms, query strings, request format specifiers, and body data.
|
||||||
|
Rocket uses attributes, which look like function decorators in other languages,
|
||||||
|
to make declaring routes easy. Routes are declared by annotating a function, the
|
||||||
|
handler, with the set of parameters to match against. A complete route
|
||||||
|
declaration looks like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/world")]
|
||||||
|
fn world() -> &'static str {
|
||||||
|
"Hello, world!"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This declares the `world` route which matches against the static path
|
||||||
|
`"/world"` for incoming `GET` requests.
|
||||||
|
|
||||||
|
## Mounting
|
||||||
|
|
||||||
|
Before Rocket dispatches requests to a route, the route needs to be _mounted_ on
|
||||||
|
an instance of `Rocket`.
|
||||||
|
|
||||||
|
Mounting a route is like namespacing it. Routes are mounted happens via the
|
||||||
|
`mount` method on a `Rocket` instance. Rocket instances can be created with the
|
||||||
|
`ignite()` static method.
|
||||||
|
|
||||||
|
The `mount` method takes **1)** a path to namespace a list of routes under, and
|
||||||
|
**2)** a list of route handlers through the `route!` macro. The `route!` macro
|
||||||
|
ties Rocket's code generation to your application. To mount the `world` route we
|
||||||
|
declared above, we would use the following code:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
rocket::ignite().mount(“/hello”, routes![world])
|
||||||
|
```
|
||||||
|
|
||||||
|
All together, this creates a new `Rocket` instance via the `ignite` function and
|
||||||
|
mounts the `world` route to the `"/hello"` path. As a result, requests to the
|
||||||
|
`"/hello/world"` path will be directed to the `world` function.
|
||||||
|
|
||||||
|
## 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 waits for
|
||||||
|
incoming requests. When a request arrives, Rocket finds the matching route and
|
||||||
|
dispatches the request to the route.
|
||||||
|
|
||||||
|
We typically call `launch` from the `main` function. Our complete _Hello,
|
||||||
|
world!_ application thus looks like:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#![feature(plugin)]
|
#![feature(plugin)]
|
||||||
|
@ -19,227 +79,321 @@ examples in [Rocket's git repository](github). Can you figure out what it does?
|
||||||
|
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
#[get("/<name>/<age>")]
|
#[get("/world")]
|
||||||
fn hello(name: &str, age: u8) -> String {
|
fn world() -> &'static str {
|
||||||
format!("Hello, {} year old named {}!", age, name)
|
"Hello, world!"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
rocket::ignite()
|
rocket::ignite().mount("/hello", routes![world]).launch();
|
||||||
.mount("/hello", routes![hello])
|
|
||||||
.launch();
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you were to run this application, your console would show:
|
Note that we've added the `#![feature(plugin)]` and `#![plugin(rocket_codegen)]`
|
||||||
|
lines to tell Rust that we'll be using Rocket's code generation plugin. We've
|
||||||
|
also imported the `rocket` crate into our namespace via `extern crate rocket`.
|
||||||
|
Finally, we call the `launch` method in the `main` function.
|
||||||
|
|
||||||
|
If we were to run the application above, our console would show:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
🔧 Configured for development.
|
🔧 Configured for development.
|
||||||
=> listening: localhost:8000
|
=> listening: localhost:8000
|
||||||
=> logging: Normal
|
=> logging: Normal
|
||||||
=> session key: false
|
=> session key: false
|
||||||
🛰 Mounting '/hello':
|
🛰 Mounting '/world':
|
||||||
=> GET /hello/<name>/<age>
|
=> GET /hello/world
|
||||||
🚀 Rocket has launched from localhost:8000...
|
🚀 Rocket has launched from localhost:8000...
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's a quick summary of what it does: first, on lines 7 - 10, it declares the
|
If we now visit `localhost:8000/hello/world`, we would see `Hello, world!`,
|
||||||
`hello` route to `GET /<name>/<age>`, which returns a `String` formatted with
|
exactly as we'd expect.
|
||||||
`name` and `age` from the dynamic path. 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 this down.
|
By the way, this example's complete crate, ready to `cargo run`, can be found on
|
||||||
|
|
||||||
We'll start with lines 1 and 2. Rocket depends on the latest version 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)]`. Lines 4 and 5 bring `rocket::Rocket` into the
|
|
||||||
namespace.
|
|
||||||
|
|
||||||
# Routes
|
|
||||||
|
|
||||||
The fun begins on line 7, where the `hello` route and request handler are
|
|
||||||
declared.
|
|
||||||
|
|
||||||
Rocket applications are composed primarily of request handlers and routes. A
|
|
||||||
_request handler_ is a function that takes an arbitrary number of arguments and
|
|
||||||
returns a response. 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 request format specifiers. Rocket uses
|
|
||||||
Rust attributes, which look like function decorators in other languages, to make
|
|
||||||
declaring routes easy. Routes are declares by annotating a function with the set
|
|
||||||
of parameters to match against. A complete route declaration looks like:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[get("/index")]
|
|
||||||
fn index() -> &str { "Hello, World!" }
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use `put`, `post`, `delete`, and `patch` in place of `get`.
|
|
||||||
|
|
||||||
## Dynamic Paths
|
|
||||||
|
|
||||||
The `hello` route declaration beginning on line 7 of our example applications
|
|
||||||
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:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[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 matching URL. 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 to the request handler in variables with matching names. This
|
|
||||||
means that `name` and `age` can be used immediately 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
|
|
||||||
_forwards_ the request to the next matching route, if any, and ultimately
|
|
||||||
returns a `404` if all of them fail. If you want to know if the user passed in a
|
|
||||||
bad `<age>`, simply use a `Result<u8, &str>` or an `Option<u8>` type for `age`
|
|
||||||
instead. For more details on routing, route collisions, and much more see the
|
|
||||||
[routing](guide/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 plenty of types in the standard library. See the
|
|
||||||
[FromParam](docs) documentation for more.
|
|
||||||
|
|
||||||
## Mounting
|
|
||||||
|
|
||||||
Now that we understand 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. If you'd like to learn more
|
|
||||||
about the `route!` macro, see the [internals guide](guide/internals).
|
|
||||||
|
|
||||||
Let's look at lines 13 - 14 again, which we reproduce below:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
rocket::ignite()
|
|
||||||
.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](/guides/mounting).
|
|
||||||
|
|
||||||
## 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:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
🛰 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](https://github.com/SergioBenitez/Rocket/tree/master/examples/hello_world).
|
[Github](https://github.com/SergioBenitez/Rocket/tree/master/examples/hello_world).
|
||||||
|
You can find dozens of other complete examples, spanning all of Rocket's
|
||||||
|
features, in the [Github examples
|
||||||
|
directory](https://github.com/SergioBenitez/Rocket/tree/master/examples/).
|
||||||
|
|
||||||
# Requests
|
# Requests
|
||||||
|
|
||||||
There's a lot more we can do with requests. The [requests](guide/requests)
|
If all we could do was match against static paths like `"/world"`, Rocket
|
||||||
chapter of the guide talks about requests in details. We'll give you a short
|
wouldn't be much fun. Of course, Rocket allows you to match against just about
|
||||||
overview of some of the more important and useful features here.
|
any information in an incoming request.
|
||||||
|
|
||||||
## Forms and Queries
|
## Dynamic Paths
|
||||||
|
|
||||||
Handling forms and query parameters couldn't be easier: declare a form or query
|
You can declare path segments as dynamic by using angle brackets around variable
|
||||||
parameter in the route attribute and handler, then ensure that its type
|
names in a route's path. For example, if we wanted to say _Hello!_ to anything,
|
||||||
implements (the automatically derivable) `FromForm`.
|
not just the world, we could declare a route and handler like so:
|
||||||
|
|
||||||
Form parameters are declared by adding `form = "<param_name>"` to the route
|
```rust
|
||||||
attribute. Say your application is processing a form submission for a new todo
|
#[get("/hello/<name>")]
|
||||||
`Task`. The form contains two fields: `complete`, a checkbox, and `description`,
|
fn hello(name: &str) -> String {
|
||||||
a text field. You can easily handle the form request in Rocket as follows:
|
format!("Hello, {}!", name)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If we were to mount the path at the root (`.mount("/", routes![hello])`), then
|
||||||
|
any request to a path with two non-empty segments, where the first segment is
|
||||||
|
`hello`, will be dispatched to the `hello` route. For example, if we were to
|
||||||
|
visit `/hello/John`, the application would respond with `Hello, John!`.
|
||||||
|
|
||||||
|
You can have any number of dynamic path segments, and the type of the path
|
||||||
|
segment can be any type that implements the [FromParam
|
||||||
|
trait](https://api.rocket.rs/rocket/request/trait.FromParam.html), including
|
||||||
|
your own! Here's a somewhat complicated route to illustrate:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/hello/<name>/<age>/<cool>")]
|
||||||
|
fn hello(name: &str, age: u8, cool: bool) -> String {
|
||||||
|
if cool {
|
||||||
|
format!("You're a cool {} year old, {}!", age, name)
|
||||||
|
} else {
|
||||||
|
format!("{}, we need to talk about your coolness.", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Forwarding
|
||||||
|
|
||||||
|
What if `cool` ain't a `bool`? Or, what if `age` isn't a `u8`? In this case, the
|
||||||
|
request is _forwarded_ to the next matching route, if there is any. This
|
||||||
|
continues until a route doesn't forward the request or there are no more routes
|
||||||
|
to try. When there are no remaining matching routes, a 404 error, which is
|
||||||
|
customizable, is returned.
|
||||||
|
|
||||||
|
Routes are tried in increasing _rank_ order. By default, routes with static
|
||||||
|
paths have a rank of 0 and routes with dynamic paths have a rank of 1. Ranks can
|
||||||
|
be manually set with the `rank` route parameter.
|
||||||
|
|
||||||
|
To illustrate, consider the following two routes:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/user/<id>")]
|
||||||
|
fn user(id: usize) -> T { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/user/<id>", rank = 2)]
|
||||||
|
fn user_str(id: &str) -> T { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice the `rank` parameter in the second route, which sets the rank of the
|
||||||
|
`user_str` route to 2. If we run this application with both routes mounted at
|
||||||
|
the root (`.mount("/", routes![user, user_str])`), requests to any route where
|
||||||
|
the `<id>` path segment is an unsigned integer will be handled by the `user`
|
||||||
|
route. If the `<id>` path segment is not an unsigned integer, the `user` route
|
||||||
|
will forward the request. Rocket will then dispatch the request to the next
|
||||||
|
matching route, `user_str`.
|
||||||
|
|
||||||
|
Forwards can be _caught_ by using a `Result` or `Option` type. For example, if
|
||||||
|
the type of `id` in the `user` function was `Result<usize, &str>`, an `Ok`
|
||||||
|
variant would indicate that `<id>` was a valid `usize`, while an `Err` would
|
||||||
|
indicate that `<id>` was not a `usize`. The `Err`'s value would contain the
|
||||||
|
string that failed to parse as a `usize`.
|
||||||
|
|
||||||
|
By the way, if you were to omit the `rank` parameter in the `user_str` route,
|
||||||
|
Rocket would emit a warning indicating that the `user` and `user_str` routes
|
||||||
|
_collide_, or can both match against an incoming request. The `rank` parameter
|
||||||
|
resolves this collision.
|
||||||
|
|
||||||
|
## Request Guards
|
||||||
|
|
||||||
|
Sometimes we need data associated with a request that isn't a direct input.
|
||||||
|
Headers and cookies are a good example of this: they simply tag along for the
|
||||||
|
ride.
|
||||||
|
|
||||||
|
Rocket makes retrieving such information easy: simply add any number of
|
||||||
|
parameters to the request handler with types that implement the `FromRequest`
|
||||||
|
trait. If the data can be retrieved from the incoming request, the handler is
|
||||||
|
called. If it cannot, the handler isn't called, and the request is forwarded on.
|
||||||
|
In this way, these parameters also act as _guards_: they protect the request
|
||||||
|
handler from being called erroneously.
|
||||||
|
|
||||||
|
For example, to retrieve cookies and the Content-Type header from a request, we
|
||||||
|
can declare a route as follows:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/")]
|
||||||
|
fn index(cookies: &Cookies, content: ContentType) -> String { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
You can implement `FromRequest` for your own types as well. For example, you
|
||||||
|
might implement `FromRequest` for an `AdminUser` type that validates that the
|
||||||
|
cookies in the incoming request authenticate an administrator. Then, any handler
|
||||||
|
with the `AdminUser` type in its argument list is assured that it will only be
|
||||||
|
invoked if an administrative user is logged in. This centralizes policies,
|
||||||
|
resulting in a simpler, safer, and more secure application.
|
||||||
|
|
||||||
|
## Data
|
||||||
|
|
||||||
|
At some point, your web application will need to process data, and Rocket makes
|
||||||
|
it as simple as possible. Data processing, like much of Rocket, is type
|
||||||
|
directed. To indicate that a handler expects data, annotate a route with a `data
|
||||||
|
= "<param>"` parameter, where `param` is an argument in the handler of a type
|
||||||
|
that implement the `FromData` trait.
|
||||||
|
|
||||||
|
### Forms
|
||||||
|
|
||||||
|
Forms are the most common type of data handled in web applications, and Rocket
|
||||||
|
makes handling them easy. 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:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
|
struct Task {
|
||||||
|
complete: bool,
|
||||||
|
description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/todo", data = "<task>")]
|
||||||
|
fn new(task: Form<Task>) -> String { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Form` type implements the `FromData` trait as long as its generic parameter
|
||||||
|
implements the `FromForm` trait. In the example, we've derived the `FromForm`
|
||||||
|
trait automatically for the `Task` structure. If a `POST /todo` request arrives,
|
||||||
|
the form data will automatically be parsed into the `Task` structure. If the
|
||||||
|
data that arrives isn't of the correct content-type, the request is forwarded.
|
||||||
|
If the data is simply invalid, a customizable `400 Bad Request` error is
|
||||||
|
returned. As before, a forward or failure can be caught by using the `Option`
|
||||||
|
and `Result` types.
|
||||||
|
|
||||||
|
### Query Strings
|
||||||
|
|
||||||
|
If you change your mind and decide to use query strings instead of `POST` forms
|
||||||
|
for the todo task, Rocket makes the transition simple: simply declare `<task>`
|
||||||
|
as a query parameter as follows:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/todo?<task>")]
|
||||||
|
fn new(task: Task) -> String { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
This works because Rocket uses the `FromForm` trait to parse structures from
|
||||||
|
query parameters as well.
|
||||||
|
|
||||||
|
### JSON
|
||||||
|
|
||||||
|
Handling JSON data is no harder: simply use the `JSON` type:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[derive(Deserialize)]
|
||||||
struct Task {
|
struct Task {
|
||||||
description: String,
|
description: String,
|
||||||
complete: bool
|
complete: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/todo", form = "<task>")]
|
#[post("/todo", data = "<task>")]
|
||||||
fn new(task: Task) -> String {
|
fn new(task: JSON<Task>) -> String { ... }
|
||||||
...
|
```
|
||||||
|
|
||||||
|
The only condition is that the generic type to `JSON` implements the
|
||||||
|
`Deserialize` trait.
|
||||||
|
|
||||||
|
### Streaming Data
|
||||||
|
|
||||||
|
Sometimes you just want to handle the incoming data directly. For example, you
|
||||||
|
might want to stream the incoming data out to a file. Rocket makes this as
|
||||||
|
simple as possible:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[post("/upload", format = "text/plain", data = "<data>")]
|
||||||
|
fn upload(data: Data) -> io::Result<Plain<String>> {
|
||||||
|
data.stream_to_file("/tmp/upload.txt").map(|n| Plain(n.to_string()))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you change your mind and want to use query strings for the form instead,
|
The route above accepts any `POST` request to the `/upload` path with
|
||||||
simple declare `<task>` as a query parameter as follows:
|
`Content-Type` `text/plain` The incoming data is streamed out to
|
||||||
|
`tmp/upload.txt` file, and the number of bytes written is returned as a plain
|
||||||
```rust
|
text response if the upload succeeds. If the upload fails, an error response is
|
||||||
#[get("/todo?<task>")]
|
returned. The handler above is complete. It really is that simple! See the
|
||||||
fn new(task: Task) -> String {
|
[Github example
|
||||||
...
|
code](https://github.com/SergioBenitez/Rocket/blob/master/examples/raw_upload/src/main.rs)
|
||||||
}
|
for the full crate.
|
||||||
```
|
|
||||||
|
|
||||||
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](guide/forms) and [queries](guide/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:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[get("/hello")]
|
|
||||||
fn hello(cookies: &Cookies) -> ..
|
|
||||||
```
|
|
||||||
|
|
||||||
## JSON
|
|
||||||
|
|
||||||
# Responses
|
# Responses
|
||||||
|
|
||||||
## Responder
|
Up until the last example, we've been returning the type of `String` from
|
||||||
|
request handlers. In fact, any type that implements the `Responder` trait can be
|
||||||
|
returned, including your own!
|
||||||
|
|
||||||
## Templates
|
## Result
|
||||||
|
|
||||||
|
One of the most common types to return is `Result`. Returning a `Result` means
|
||||||
|
one of two things: If the error type tself implements `Responder`, the response
|
||||||
|
will come from either the `Ok` or `Err` value, whichever the variant is. If the
|
||||||
|
error type does _not_ implement `Responder`, a customizable internal server
|
||||||
|
error will be returned.
|
||||||
|
|
||||||
## JSON
|
## JSON
|
||||||
|
|
||||||
|
Responding with JSON data is just as simple: simply return a JSON type. For
|
||||||
|
example, to respond with the JSON value of the `Task` structure from previous
|
||||||
|
examples, we would write:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Task { ... }
|
||||||
|
|
||||||
|
#[get("/todo")]
|
||||||
|
fn todo() -> JSON<Task> { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the generic type for the JSON response type must implement
|
||||||
|
`Serialize`.
|
||||||
|
|
||||||
|
## Templates
|
||||||
|
|
||||||
|
Rocket has built-in support for templating. To respond with a rendered template,
|
||||||
|
simply return a `Template` type:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/")]
|
||||||
|
fn index() -> Template {
|
||||||
|
let context = ...;
|
||||||
|
Template::render("index", &context)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `render` static method takes in the name of a template (here, `"index"`) and
|
||||||
|
a value to use as the _context_ for the template's rendering. The context must
|
||||||
|
contain all of the parameters expected by the template.
|
||||||
|
|
||||||
|
Templating support in Rocket is engine agnostic. The engine used to render a
|
||||||
|
template depends on the template file's extension. For example, if a file ends
|
||||||
|
with `.hbs`, Handlebars is used, while if a file ends with `.tera`, Tera is
|
||||||
|
used.
|
||||||
|
|
||||||
|
## Streaming
|
||||||
|
|
||||||
|
When a large amount of data is to be returned, it is often better to stream the
|
||||||
|
data to the client so as to avoid consuming large amounts of memory. Rocket
|
||||||
|
provides the `Stream` type to accomplish this. The `Stream` type can be created
|
||||||
|
from any `Read` type. For example, to stream from a local Unix stream, we might
|
||||||
|
write:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/stream")]
|
||||||
|
fn stream() -> io::Result<Stream<UnixStream>> {
|
||||||
|
let mut unix = UnixStream::connect("/path/to/my/socket")?;
|
||||||
|
Stream::from(unix)
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Rocket takes care of the rest.
|
||||||
|
|
||||||
# What's next?
|
# What's next?
|
||||||
|
|
||||||
|
That was just a taste of what Rocket has to offer! There's so much more:
|
||||||
|
|
||||||
|
* [Quickstart](guide/quickstart): How to get started as quickly as possible.
|
||||||
|
* [Getting Started](guide/getting_started): How to start your first project.
|
||||||
|
* [Overview](overview): A brief introduction.
|
||||||
|
* [Guide](guide): A detailed guide and reference to every component.
|
||||||
|
* [API Documentation](https://api.rocket.rs): The "rustdocs" (API documentation).
|
||||||
|
|
Loading…
Reference in New Issue