mirror of https://github.com/rwf2/Rocket.git
206 lines
7.9 KiB
Markdown
206 lines
7.9 KiB
Markdown
# Fairings
|
|
|
|
Fairings are Rocket's approach to structured middleware. They allow for
|
|
interposition at various points in the application and request/response
|
|
lifecycle through callbacks issued by Rocket.
|
|
|
|
## Overview
|
|
|
|
A _fairing_ is any type that implements the [`Fairing`] trait. The `Fairing`
|
|
trait is composed of methods that represent callbacks that Rocket will run at
|
|
requested points in a program. Through these methods, fairings can rewrite or
|
|
record information about requests and responses as well as perform actions when
|
|
a Rocket application launches.
|
|
|
|
[`Fairing`]: https://api.rocket.rs/rocket/fairing/trait.Fairing.html
|
|
|
|
### Attaching
|
|
|
|
For a fairing to be active, it must first be _attached_ through the the
|
|
[`attach`] method on a [`Rocket`] instance. For instance, to attach fairings
|
|
named `req_fairing` and `res_fairing` to a new Rocket instance, you might write:
|
|
|
|
```rust
|
|
rocket::ignite()
|
|
.attach(req_fairing)
|
|
.attach(res_fairing)
|
|
.launch();
|
|
```
|
|
|
|
Once a fairing is attached, Rocket will execute its callbacks at the appropiate
|
|
time.
|
|
|
|
[`attach`]: https://api.rocket.rs/rocket/struct.Rocket.html#method.attach
|
|
[`Rocket`]: https://api.rocket.rs/rocket/struct.Rocket.html
|
|
|
|
### Callbacks
|
|
|
|
A fairing can implement any combination of the following four callbacks:
|
|
|
|
* **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.
|
|
|
|
* **Launch**
|
|
|
|
A launch callback is called immediately before the Rocket application has
|
|
launched. A launch callback can inspect the `Rocket` instance being
|
|
launched.
|
|
|
|
* **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**
|
|
|
|
A response callback is called when a response is ready to be sent to the
|
|
client. A response callback can modify the response at will. For example, a
|
|
response callback can provide a default response when the user fails to
|
|
handle the request by checking for 404 responses.
|
|
|
|
|
|
### Execution Order
|
|
|
|
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.
|
|
|
|
### 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 to create an `AdHoc` structure from
|
|
a function or closure. Then, attach the structure to a `Rocket` instance. Rocket
|
|
takes care of the rest.
|
|
|
|
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
|
|
|
|
## Considerations
|
|
|
|
Fairings are a large hammer that can easily be abused and misused. If you
|
|
are considering writing a `Fairing` implementation, first consider if it is
|
|
appropriate to do so. While middleware is often the best solution to some
|
|
problems in other frameworks, it is often a suboptimal solution in Rocket.
|
|
This is because Rocket provides richer mechanisms such as [request guards]
|
|
and [data guards] that can be used to accomplish the same objective in a
|
|
cleaner, more composable, and more robust manner.
|
|
|
|
As a general rule of thumb, only _globally applicable actions_ should be
|
|
implemented via fairings. For instance, 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 to implement global security policies.
|
|
|
|
[request guard]: https://api.rocket.rs/rocket/request/trait.FromRequest.html
|
|
[request guards]: https://api.rocket.rs/rocket/request/trait.FromRequest.html
|
|
[data guards]: https://api.rocket.rs/rocket/data/trait.FromData.html
|
|
|
|
## Implementing
|
|
|
|
A fairing must implement 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
|
|
which callbacks to actually issue on the `Fairing`. A `Fairing` can also
|
|
implement any of the available callbacks: [`on_attach`], [`on_launch`],
|
|
[`on_request`], and [`on_response`].
|
|
|
|
[`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
|
|
|
|
### Restrictions
|
|
|
|
A `Fairing` must 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 `Counter` fairing below records the number of all `GET` and `POST` requests
|
|
received. It makes these counts available at a special `'/counts'` path.
|
|
|
|
```rust
|
|
struct Counter {
|
|
get: AtomicUsize,
|
|
post: AtomicUsize,
|
|
}
|
|
|
|
impl Fairing for Counter {
|
|
fn info(&self) -> Info {
|
|
Info {
|
|
name: "GET/POST Counter",
|
|
kind: Kind::Request | Kind::Response
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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).
|