mirror of https://github.com/rwf2/Rocket.git
219 lines
9.1 KiB
Markdown
219 lines
9.1 KiB
Markdown
# Fairings
|
|
|
|
Fairings are Rocket's approach to structured middleware. With fairings, your
|
|
application can hook into the request lifecycle to record or rewrite information
|
|
about incoming requests and outgoing responses.
|
|
|
|
## Overview
|
|
|
|
A _fairing_ is a special name for a type that implements the [`Fairing`] trait.
|
|
Fairings receive callbacks from Rocket when certain events, like that of an
|
|
incoming request, occur. Rocket passes information about the event to the
|
|
fairing, and the fairing can do what it wants with the information. This
|
|
includes rewriting data when applicable, recording information about the event
|
|
or data, or doing nothing at all.
|
|
|
|
Fairings are a lot like middleware in other frameworks with a few key
|
|
distinctions:
|
|
|
|
* Fairings **cannot** terminate or respond to an incoming request directly.
|
|
* Fairings **cannot** inject arbitrary, non-request data into a request.
|
|
* Fairings _can_ prevent an application from launching.
|
|
* Fairings _can_ inspect and modify the application's configuration.
|
|
|
|
If you are familiar with middleware from other frameworks, you may find yourself
|
|
reaching for fairings instinctively. Before doing so, consider whether Rocket
|
|
provides a better solution to your problem: While middleware may be the best
|
|
solution to a problem in another framework, it is often a suboptimal solution in
|
|
Rocket. Rocket provides richer mechanisms such as [request guards] and [data
|
|
guards] that can be used to solve problems in a cleaner, more composable, and
|
|
more robust manner.
|
|
|
|
As a general rule of thumb, only _globally applicable_ actions should be
|
|
effected through fairings. You should _not_ use a fairing to implement
|
|
authentication or authorization (preferring to use a [request guard] instead)
|
|
_unless_ the authentication or authorization applies to the entire application.
|
|
On the other hand, you _should_ use a fairing to record timing and/or usage
|
|
statistics or global security policies.
|
|
|
|
[`Fairing`]: https://api.rocket.rs/rocket/fairing/trait.Fairing.html
|
|
[request guard]: /guide/requests/#request-guards
|
|
[request guards]: /guide/requests/#request-guards
|
|
[data guards]: /guide/requests/#body-data
|
|
|
|
### Attaching
|
|
|
|
Fairings are registered with Rocket via the [`attach`] method on a [`Rocket`]
|
|
instance. Only when a fairing is attached will its callbacks fire. As an
|
|
example, the following snippet attached two fairings, `req_fairing` and
|
|
`res_fairing`, to a new Rocket instance:
|
|
|
|
```rust
|
|
rocket::ignite()
|
|
.attach(req_fairing)
|
|
.attach(res_fairing)
|
|
.launch();
|
|
```
|
|
|
|
[`attach`]: https://api.rocket.rs/rocket/struct.Rocket.html#method.attach
|
|
[`Rocket`]: https://api.rocket.rs/rocket/struct.Rocket.html
|
|
|
|
Fairings are executed in the order in which they are attached: the first
|
|
attached fairing has its callbacks executed before all others. Because fairing
|
|
callbacks may not be commutative, the order in which fairings are attached may
|
|
be significant.
|
|
|
|
### Callbacks
|
|
|
|
There are four events for which Rocket issues fairing callbacks. Each of these
|
|
events is described below:
|
|
|
|
* **Attach (`on_attach`)**
|
|
|
|
An attach callback is called when a fairing is first attached via the
|
|
[`attach`](https://api.rocket.rs/rocket/struct.Rocket.html#method.attach)
|
|
method. An attach callback can arbitrarily modify the `Rocket` instance
|
|
being constructed and optionally abort launch. Attach fairings are commonly
|
|
used to parse and validate configuration values, aborting on bad
|
|
configurations, and inserting the parsed value into managed state for later
|
|
retrieval.
|
|
|
|
* **Launch (`on_launch`)**
|
|
|
|
A launch callback is called immediately before the Rocket application has
|
|
launched. A launch callback can inspect the `Rocket` instance being
|
|
launched. A launch callback can be a convenient hook for launching services
|
|
related to the Rocket application being launched.
|
|
|
|
* **Request (`on_request`)**
|
|
|
|
A request callback is called just after a request is received. A request
|
|
callback can modify the request at will and peek into the incoming data. It
|
|
may not, however, abort or respond directly to the request; these issues are
|
|
better handled via request guards or via response callbacks.
|
|
|
|
* **Response (`on_response`)**
|
|
|
|
A response callback is called when a response is ready to be sent to the
|
|
client. A response callback can modify part or all of the response. As such,
|
|
a response fairing can be used to provide a response when the greater
|
|
applications fails to by rewriting **404** responses as desired. As another
|
|
example, response fairings can also be used to inject headers into all
|
|
outgoing responses.
|
|
|
|
## Implementing
|
|
|
|
Recall that a fairing is any type that implements the [`Fairing`] trait. A
|
|
`Fairing` implementation has one required method: [`info`], which returns an
|
|
[`Info`] structure. This structure is used by Rocket to assign a name to the
|
|
fairing and determine the set of callbacks the fairing is registering for. A
|
|
`Fairing` can implement any of the available callbacks: [`on_attach`],
|
|
[`on_launch`], [`on_request`], and [`on_response`]. Each callback has a default
|
|
implementation that does absolutely nothing.
|
|
|
|
[`Info`]: https://api.rocket.rs/rocket/fairing/struct.Info.html
|
|
[`info`]: https://api.rocket.rs/rocket/fairing/trait.Fairing.html#tymethod.info
|
|
[`on_attach`]: https://api.rocket.rs/rocket/fairing/trait.Fairing.html#method.on_attach
|
|
[`on_launch`]: https://api.rocket.rs/rocket/fairing/trait.Fairing.html#method.on_launch
|
|
[`on_request`]: https://api.rocket.rs/rocket/fairing/trait.Fairing.html#method.on_request
|
|
[`on_response`]: https://api.rocket.rs/rocket/fairing/trait.Fairing.html#method.on_response
|
|
|
|
### Requirements
|
|
|
|
A type implementing `Fairing` is required to be `Send + Sync + 'static`. This
|
|
means that the fairing must be sendable across thread boundaries (`Send`),
|
|
thread-safe (`Sync`), and have only static references, if any (`'static`). Note
|
|
that these bounds _do not_ prohibit a `Fairing` from holding state: the state
|
|
need simply be thread-safe and statically available or heap allocated.
|
|
|
|
### Example
|
|
|
|
Imagine that we want to record the number of `GET` and `POST` requests that our
|
|
application has received. While we could do this with request guards and managed
|
|
state, it would require us to annotate every `GET` and `POST` request with
|
|
custom types, polluting handler signatures. Instead, we can create a simple
|
|
fairing that acts globally.
|
|
|
|
The code for a `Counter` fairing below implements exactly this. The fairing
|
|
receives a request callback, where it increments a counter on each `GET` and
|
|
`POST` request. It also receives a response callback, where it responds to
|
|
unrouted requests to the `/counts` path by returning the recorded number of
|
|
counts.
|
|
|
|
```rust
|
|
struct Counter {
|
|
get: AtomicUsize,
|
|
post: AtomicUsize,
|
|
}
|
|
|
|
impl Fairing for Counter {
|
|
// This is a request and respone fairing named "GET/POST Counter".
|
|
fn info(&self) -> Info {
|
|
Info {
|
|
name: "GET/POST Counter",
|
|
kind: Kind::Request | Kind::Response
|
|
}
|
|
}
|
|
|
|
// Increment the counter for `GET` and `POST` requests.
|
|
fn on_request(&self, request: &mut Request, _: &Data) {
|
|
match request.method() {
|
|
Method::Get => self.get.fetch_add(1, Ordering::Relaxed),
|
|
Method::Post => self.post.fetch_add(1, Ordering::Relaxed),
|
|
_ => return
|
|
}
|
|
}
|
|
|
|
fn on_response(&self, request: &Request, response: &mut Response) {
|
|
// Don't change a successful user's response, ever.
|
|
if response.status() != Status::NotFound {
|
|
return
|
|
}
|
|
|
|
// Rewrite the response to return the current counts.
|
|
if request.method() == Method::Get && request.uri().path() == "/counts" {
|
|
let get_count = self.get.load(Ordering::Relaxed);
|
|
let post_count = self.post.load(Ordering::Relaxed);
|
|
let body = format!("Get: {}\nPost: {}", get_count, post_count);
|
|
|
|
response.set_status(Status::Ok);
|
|
response.set_header(ContentType::Plain);
|
|
response.set_sized_body(Cursor::new(body));
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
For brevity, imports are not shown. The complete example can be found in the
|
|
[`Fairing`
|
|
documentation](https://api.rocket.rs/rocket/fairing/trait.Fairing.html#example).
|
|
|
|
## Ad-Hoc Fairings
|
|
|
|
For simple occasions, implementing the `Fairing` trait can be cumbersome. This
|
|
is why Rocket provides the [`AdHoc`] type, which creates a fairing from a simple
|
|
function or clusure. Using the `AdHoc` type is easy: simply call the
|
|
`on_attach`, `on_launch`, `on_request`, or `on_response` constructors on `AdHoc`
|
|
to create an `AdHoc` structure from a function or closure.
|
|
|
|
As an example, the code below creates a `Rocket` instance with two attached
|
|
ad-hoc fairings. The first, a launch fairing, simply prints a message indicating
|
|
that the application is about to the launch. The second, a request fairing,
|
|
changes the method of all requests to `PUT`.
|
|
|
|
```rust
|
|
use rocket::fairing::AdHoc;
|
|
use rocket::http::Method;
|
|
|
|
rocket::ignite()
|
|
.attach(AdHoc::on_launch(|_| {
|
|
println!("Rocket is about to launch! Exciting!");
|
|
}))
|
|
.attach(AdHoc::on_request(|req, _| {
|
|
req.set_method(Method::Put);
|
|
}));
|
|
```
|
|
|
|
[`AdHoc`]: https://api.rocket.rs/rocket/fairing/enum.AdHoc.html
|
|
|