Rocket/site/guide/3-overview.md

182 lines
6.0 KiB
Markdown

# Overview
Rocket provides primitives to build web servers and applications with Rust:
Rocket provides routing, pre-processing of requests, and post-processing of
responses; the rest is up to you. Your application code instructs Rocket on 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 requests, dispatch the request
to the application code, and return a response to the client. We call the
process that goes from request to response the "lifecycle". We summarize the
lifecycle as the following sequence of steps:
1. **Routing**
Rocket parses an incoming HTTP request into native structures that your
code operates on indirectly. Rocket determines which request handler to
invoke by matching against route attributes declared in your application.
2. **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_.
3. **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`.
4. **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 requests, restarting the lifecycle for each
incoming request.
The remainder of this section details the _routing_ phase as well as additional
components needed for Rocket to begin dispatching requests to request handlers.
The sections following describe the request and response phases as well as other
components of Rocket.
## 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 arbitrary number of arguments and
returns any arbitrary type.
The parameters to match against include 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")] // <- route attribute
fn world() -> &'static str { // <- request handler
"Hello, world!"
}
```
This declares 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 applications. The
[Requests](../requests) section describes the available options for
constructing routes.
## Mounting
Before Rocket can dispatch requests to a route, the route needs to be _mounted_:
```rust
fn main() {
rocket::ignite().mount("/hello", routes![world]);
}
```
The `mount` method takes as input:
1. A _base_ path to namespace a list of routes under.
2. A list of routes via the `routes!` macro.
This creates a new `Rocket` instance via the `ignite` function and mounts the
`world` route to the `"/hello"` path, making Rocket aware of the route. `GET`
requests to `"/hello/world"` will be directed to the `world` function.
! note: In many cases, the base path will simply be `"/"`.
### Namespacing
When a route is declared inside a module other than the root, you may find
yourself with unexpected errors when mounting:
```rust
mod other {
#[get("/world")]
pub fn world() -> &'static str {
"Hello, world!"
}
}
use other::world;
fn main() {
// error[E0425]: cannot find value `static_rocket_route_info_for_world` in this scope
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 refer to the route using a namespaced path instead:
```rust
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 waits for
incoming requests. When a request arrives, Rocket finds the matching route and
dispatches the request to the route's handler.
We typically call `launch` from the `main` function. Our complete _Hello,
world!_ application thus looks like:
```rust
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate rocket;
#[get("/world")]
fn world() -> &'static str {
"Hello, world!"
}
fn main() {
rocket::ignite().mount("/hello", routes![world]).launch();
}
```
Note the `#![feature]` line: this tells Rust that we're opting in to compiler
features available in the nightly release channel. This line **must** be in the
crate root, typically `main.rs`. We've also imported the `rocket` crate and all
of its macros into our namespace via `#[macro_use] extern crate rocket`.
Finally, we call the `launch` method in the `main` function.
Running the application, the console shows:
```sh
🔧 Configured for development.
=> address: localhost
=> port: 8000
=> log: normal
=> workers: [logical cores * 2]
=> secret key: generated
=> limits: forms = 32KiB
=> keep-alive: 5s
=> tls: disabled
🛰 Mounting '/':
=> GET / (hello)
🚀 Rocket has launched from http://localhost:8000
```
If we visit `localhost:8000/hello/world`, we see `Hello, world!`, exactly as we
expected.
A version of this example's complete crate, ready to `cargo run`, can be found
on [GitHub](@example/hello_world). You can find dozens of other complete
examples, spanning all of Rocket's features, in the [GitHub examples
directory](@example/).