mirror of https://github.com/rwf2/Rocket.git
Rewrite the 'Responses' guide for 0.3.
This commit is contained in:
parent
436bd99376
commit
a61977befc
|
@ -1,82 +1,69 @@
|
|||
# Responses
|
||||
|
||||
You may have noticed that the return type of a handler appears to be arbitrary,
|
||||
and that's because it is! A value of any type that implements the
|
||||
[Responder](https://api.rocket.rs/rocket/response/trait.Responder.html) trait
|
||||
can be returned, including your own.
|
||||
and that's because it is! A value of any type that implements the [`Responder`]
|
||||
trait can be returned, including your own. In this section, we describe the
|
||||
`Responder` trait as well as several useful `Responder`s provided by Rocket.
|
||||
We'll also briefly discuss how to implement your own `Responder`.
|
||||
|
||||
[`Responder`]: https://api.rocket.rs/rocket/response/trait.Responder.html
|
||||
|
||||
## Responder
|
||||
|
||||
Types that implement `Responder` know how to generate a
|
||||
[Response](https://api.rocket.rs/rocket/response/struct.Response.html) from
|
||||
their values. A `Response` includes the HTTP status, headers, and body of the
|
||||
response. Rocket implements `Responder` for many built-in types including
|
||||
`String`, `&str`, `File`, `Option`, `Result`, and others. Rocket also provides
|
||||
custom types, such as
|
||||
[Content](https://api.rocket.rs/rocket/response/struct.Content.html) and
|
||||
[Flash](https://api.rocket.rs/rocket/response/struct.Flash.html), which you can
|
||||
find in the [response](https://api.rocket.rs/rocket/response/index.html) module.
|
||||
Types that implement [`Responder`] know how to generate a [`Response`] from
|
||||
their values. A `Response` includes an HTTP status, headers, and body. The body
|
||||
may either be _fixed-sized_ or _streaming_. The given `Responder` implementation
|
||||
decides which to use. For instance, `String` uses a fixed-sized body, while
|
||||
`File` uses a streamed response. Responders may dynamically adjust their
|
||||
responses according to the incoming `Request` they are responding to.
|
||||
|
||||
The body of a `Response` may either be _fixed-sized_ or _streaming_. The given
|
||||
`Responder` implementation decides which to use. For instance, `String` uses a
|
||||
fixed-sized body, while `File` uses a streaming body.
|
||||
[`Response`]: https://api.rocket.rs/rocket/response/struct.Response.html
|
||||
|
||||
### Wrapping
|
||||
|
||||
Responders can _wrap_ other responders. That is, responders can be of the
|
||||
following form, where `R: Responder`:
|
||||
Before we describe a few responders, we note that it is typical for responders
|
||||
to _wrap_ other responders. That is, responders can be of the following form,
|
||||
where `R` is some type that implements `Responder`:
|
||||
|
||||
```rust
|
||||
struct WrappingResponder<R>(R);
|
||||
```
|
||||
|
||||
When this is the case, the wrapping responder will modify the response returned
|
||||
by `R` in some way before responding itself. For instance, to override the
|
||||
status code of some response, you can use the types in the [status
|
||||
module](https://api.rocket.rs/rocket/response/status/index.html). In particular,
|
||||
to set the status code of a response for a `String` to **202 Accepted**, you can
|
||||
return a type of `status::Accepted<String>`:
|
||||
A wrapping responder modifies the response returned by `R` before responding
|
||||
with that same response. For instance, Rocket provides `Responder`s in the
|
||||
[`status` module](https://api.rocket.rs/rocket/response/status/index.html) that
|
||||
override the status code of the wrapped `Responder`. As an example, the
|
||||
[`Accepted`] type sets the status to `202 - Accepted`. It can be used as
|
||||
follows:
|
||||
|
||||
```rust
|
||||
#[get("/")]
|
||||
fn accept() -> status::Accepted<String> {
|
||||
status::Accepted("I accept!".to_string())
|
||||
use rocket::response::status;
|
||||
|
||||
#[post("/<id>")]
|
||||
fn new(id: usize) -> status::Accepted<String> {
|
||||
let url = "http://example.com/resource.json";
|
||||
status::Created(url.into(), Some(format!("id: '{}'", id)))
|
||||
}
|
||||
```
|
||||
|
||||
By default, the `String` responder sets the status to **200**. By using the
|
||||
`Accepted` type however, The client will receive an HTTP response with status
|
||||
code **202**.
|
||||
|
||||
Similarly, the types in the [content
|
||||
Similarly, the types in the [`content`
|
||||
module](https://api.rocket.rs/rocket/response/content/index.html) can be used to
|
||||
override the Content-Type of the response. For instance, to set the Content-Type
|
||||
of some `&'static str` to JSON, you can use the
|
||||
[content::JSON](https://api.rocket.rs/rocket/response/content/struct.JSON.html)
|
||||
type as follows:
|
||||
override the Content-Type of a response. For instance, to set the Content-Type
|
||||
an `&'static str` to JSON, you can use the [`content::JSON`] type as follows:
|
||||
|
||||
```rust
|
||||
use rocket::response::content;
|
||||
|
||||
#[get("/")]
|
||||
fn json() -> content::JSON<&'static str> {
|
||||
content::JSON("{ 'hi': 'world' }")
|
||||
}
|
||||
```
|
||||
|
||||
### Result
|
||||
[`Accepted`]: https://api.rocket.rs/rocket/response/status/struct.Accepted.html
|
||||
[`content::JSON`]: https://api.rocket.rs/rocket/response/content/struct.JSON.html
|
||||
|
||||
`Result` is one of the most commonly used responders. Returning a `Result` means
|
||||
one of two things. If the error type implements `Responder`, the `Ok` or `Err`
|
||||
value will be used, whichever the variant is. If the error type does _not_
|
||||
implement `Responder`, the error is printed to the console, and the request is
|
||||
forwarded to the **500** error catcher.
|
||||
|
||||
### Option
|
||||
|
||||
`Option` is another commonly used responder. If the `Option` is `Some`, the
|
||||
wrapped responder is used to respond to the client. Otherwise, the request is
|
||||
forwarded to the **404** error catcher.
|
||||
|
||||
## Errors
|
||||
### Errors
|
||||
|
||||
Responders may fail; they need not _always_ generate a response. Instead, they
|
||||
can return an `Err` with a given status code. When this happens, Rocket forwards
|
||||
|
@ -87,16 +74,13 @@ If an error catcher has been registered for the given status code, Rocket will
|
|||
invoke it. The catcher creates and returns a response to the client. If no error
|
||||
catcher has been registered and the error status code is one of the standard
|
||||
HTTP status code, a default error catcher will be used. Default error catchers
|
||||
returns an HTML page with the status code and description.
|
||||
|
||||
If there is no catcher for a custom status code, Rocket uses the **500** error
|
||||
catcher to return a response.
|
||||
|
||||
### Failure
|
||||
return an HTML page with the status code and description. If there is no catcher
|
||||
for a custom status code, Rocket uses the **500** error catcher to return a
|
||||
response.
|
||||
|
||||
While not encouraged, you can also forward a request to a catcher manually by
|
||||
using the [Failure](https://api.rocket.rs/rocket/response/struct.Failure.html)
|
||||
type. For instance, to forward to the catcher for **406 Not Acceptable**, you
|
||||
using the [`Failure`](https://api.rocket.rs/rocket/response/struct.Failure.html)
|
||||
type. For instance, to forward to the catcher for **406 - Not Acceptable**, you
|
||||
would write:
|
||||
|
||||
```rust
|
||||
|
@ -106,75 +90,114 @@ fn just_fail() -> Failure {
|
|||
}
|
||||
```
|
||||
|
||||
## JSON
|
||||
## `std` Implementations
|
||||
|
||||
Responding with JSON data is simple: return a value of type
|
||||
[JSON](https://api.rocket.rs/rocket_contrib/struct.JSON.html). For example, to
|
||||
respond with the JSON value of the `Task` structure from previous examples, we
|
||||
would write:
|
||||
Rocket implements `Responder` for many types in Rust's standard library
|
||||
including `String`, `&str`, `File`, `Option`, and `Result`. The [`Responder`]
|
||||
documentation describes these in detail, but we briefly cover a few here.
|
||||
|
||||
### `&str` and `String`
|
||||
|
||||
The `Responder` implementations for `&str` and `String` are straight-forward:
|
||||
the string is used as a sized body, and the Content-Type of the response is set
|
||||
to `text/plain`. To get a taste for what such a `Responder` implementation looks
|
||||
like, here's the implementation for `String`:
|
||||
|
||||
```rust
|
||||
#[derive(Serialize)]
|
||||
struct Task { ... }
|
||||
|
||||
#[get("/todo")]
|
||||
fn todo() -> JSON<Task> { ... }
|
||||
```
|
||||
|
||||
The generic type in `JSON` must implement `Serialize`. The `JSON` type
|
||||
serializes the structure into JSON, sets the Content-Type to JSON, and emits the
|
||||
serialization in a fixed-sized body. If serialization fails, the request is
|
||||
forwarded to the **500** error catcher.
|
||||
|
||||
For a complete example, see the [JSON example on
|
||||
GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/json).
|
||||
|
||||
## Templates
|
||||
|
||||
Rocket has built-in support for templating. To respond with a rendered template,
|
||||
ensure that you are using
|
||||
[`Template::fairing()`](https://api.rocket.rs/rocket_contrib/struct.Template.html#method.fairing)
|
||||
and then simply return a
|
||||
[Template](https://api.rocket.rs/rocket_contrib/struct.Template.html) type.
|
||||
|
||||
```rust
|
||||
#[get("/")]
|
||||
fn index() -> Template {
|
||||
let context = /* object-like value */;
|
||||
Template::render("index", &context)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
rocket::ignite()
|
||||
.mount("/", routes![index])
|
||||
.attach(Template::fairing())
|
||||
.launch();
|
||||
impl Responder<'static> for String {
|
||||
fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> {
|
||||
Response::build()
|
||||
.header(ContentType::Plain)
|
||||
.sized_body(Cursor::new(self))
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Templates are rendered with the `render` method. The method takes in the name of
|
||||
a template and a context to render the template with. Rocket searches for a
|
||||
template with that name in the configurable `template_dir` configuration
|
||||
parameter, which defaults to `templates/`. 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.
|
||||
Because of these implementations, you can directly return an `&str` or `String`
|
||||
type from a handler:
|
||||
|
||||
The context can be any type that implements `Serialize` and serializes to an
|
||||
`Object` value, such as structs, `HashMaps`, and others. The
|
||||
[Template](https://api.rocket.rs/rocket_contrib/struct.Template.html) API
|
||||
documentation contains more information about templates, while the [Handlebars
|
||||
Templates example on
|
||||
GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/handlebars_templates)
|
||||
is a fully composed application that makes use of Handlebars templates.
|
||||
```rust
|
||||
#[get("/string")]
|
||||
fn handler() -> &'static str {
|
||||
"Hello there! I'm a string!"
|
||||
}
|
||||
```
|
||||
|
||||
## Streaming
|
||||
### `Option<T>` **where** `T: Responder`
|
||||
|
||||
When a large amount of data needs to be sent to the client, it is better to
|
||||
stream the data to the client to avoid consuming large amounts of memory. Rocket
|
||||
provides the [Stream](https://api.rocket.rs/rocket/response/struct.Stream.html)
|
||||
type, making this easy. The `Stream` type can be created from any `Read` type.
|
||||
For example, to stream from a local Unix stream, we might write:
|
||||
`Option` is _wrapping_ responder: an `Option<T>` can only be returned when `T`
|
||||
implements `Responder`. If the `Option` is `Some`, the wrapped responder is used
|
||||
to respond to the client. Otherwise, a error of **404 - Not Found** is returned
|
||||
to the client.
|
||||
|
||||
This implementation makes `Option` a convenient type to return when it is not
|
||||
known until process-time whether content exists. For example, because of
|
||||
`Option`, we can implement a file server that returns a `200` when a file is
|
||||
found and a `404` when a file is not found in just 4, idiomatic lines:
|
||||
|
||||
```rust
|
||||
#[get("/<file..>")]
|
||||
fn files(file: PathBuf) -> Option<NamedFile> {
|
||||
NamedFile::open(Path::new("static/").join(file)).ok()
|
||||
}
|
||||
```
|
||||
|
||||
### `Result<T, E>` **where** `E: Debug`, `E: Responder`
|
||||
|
||||
`Result` is a special kind of wrapping responder: its functionality depends on
|
||||
whether the error type `E` implements `Responder`.
|
||||
|
||||
When the error type `E` implements `Responder`, the wrapped `Responder` in `Ok`
|
||||
or `Err`, whichever it might be, is used to respond to the client. This means
|
||||
that the responder can be chosen dynamically at run-time, and two different
|
||||
kinds of responses can be used depending on the circumstances. Revisting our
|
||||
file server, for instance, we might wish to provide more feedback to the user
|
||||
when a file isn't found. We might do this as follows:
|
||||
|
||||
```rust
|
||||
use rocket::response::status::NotFound;
|
||||
|
||||
#[get("/<file..>")]
|
||||
fn files(file: PathBuf) -> Result<NamedFile, NotFound<String>> {
|
||||
let path = Path::new("static/").join(file);
|
||||
NamedFile::open(&path).map_err(|_| NotFound(format!("Bad path: {}", path)))
|
||||
}
|
||||
```
|
||||
|
||||
If the error type `E` _does not_ implement `Responder`, then the error is simply
|
||||
logged to the console, using its `Debug` implementation, and a `500` error is
|
||||
returned to the client.
|
||||
|
||||
## Rocket Responders
|
||||
|
||||
Some of Rocket's best features are implemented through responders. You can find
|
||||
many of these responders in the [`response`] module. Among these are:
|
||||
|
||||
* [`Content`] - Used to override the Content-Type of a response.
|
||||
* [`NamedFile`] - Streams a file to the client; automatically sets the
|
||||
Content-Type based on the file's extension.
|
||||
* [`Redirect`] - Redirects the client to a different URI.
|
||||
* [`Stream`] - Streams a response to a client from an arbitrary `Read`er type.
|
||||
* [`status`] - Contains types that override the status code of a response.
|
||||
* [`Flash`] - Sets a "flash" cookie that is removed when accessed.
|
||||
|
||||
[`status`]: https://api.rocket.rs/rocket/response/status/index.html
|
||||
[`response`]: https://api.rocket.rs/rocket/response/index.html
|
||||
[`NamedFile`]: https://api.rocket.rs/rocket/response/struct.NamedFile.html
|
||||
[`Content`]: https://api.rocket.rs/rocket/response/struct.Content.html
|
||||
[`Redirect`]: https://api.rocket.rs/rocket/response/struct.Redirect.html
|
||||
[`Stream`]: https://api.rocket.rs/rocket/response/struct.Stream.html
|
||||
[`Flash`]: https://api.rocket.rs/rocket/response/struct.Flash.html
|
||||
|
||||
### Streaming
|
||||
|
||||
The `Stream` type deserves special attention. When a large amount of data needs
|
||||
to be sent to the client, it is better to stream the data to the client to avoid
|
||||
consuming large amounts of memory. Rocket provides the
|
||||
[Stream](https://api.rocket.rs/rocket/response/struct.Stream.html) type, making
|
||||
this easy. 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")]
|
||||
|
@ -184,4 +207,81 @@ fn stream() -> io::Result<Stream<UnixStream>> {
|
|||
|
||||
```
|
||||
|
||||
Rocket takes care of the rest.
|
||||
[`rocket_contrib`]: https://api.rocket.rs/rocket_contrib/index.html
|
||||
|
||||
### JSON
|
||||
|
||||
The [`JSON`] responder in [`rocket_contrib`] allows you to easily respond with
|
||||
well-formed JSON data: simply return a value of type `JSON<T>` where `T` is the
|
||||
type of a structure to serialize into JSON. The type `T` must implement the
|
||||
[`Serialize`] trait from [`serde`], which can be automatically derived.
|
||||
|
||||
An an example, to respond with the JSON value of a `Task` structure, we might
|
||||
write:
|
||||
|
||||
```rust
|
||||
use rocket_contrib::JSON;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Task { ... }
|
||||
|
||||
#[get("/todo")]
|
||||
fn todo() -> JSON<Task> { ... }
|
||||
```
|
||||
|
||||
The `JSON` type serializes the structure into JSON, sets the Content-Type to
|
||||
JSON, and emits the serialized data in a fixed-sized body. If serialization
|
||||
fails, a **500 - Internal Server Error** is returned.
|
||||
|
||||
The [JSON example on GitHub] provides further illustration.
|
||||
|
||||
[`JSON`]: https://api.rocket.rs/rocket_contrib/struct.JSON.html
|
||||
[`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
|
||||
[`serde`]: https://docs.serde.rs/serde/
|
||||
[JSON example on GitHub]: https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/json
|
||||
|
||||
### Templates
|
||||
|
||||
Rocket includes built-in templating support that works largely through a
|
||||
[`Template`] responder in `rocket_contrib`. To render a template named "index",
|
||||
for instance, you might return a value of type `Template` as follows:
|
||||
|
||||
```rust
|
||||
#[get("/")]
|
||||
fn index() -> Template {
|
||||
let context = /* object-like value */;
|
||||
Template::render("index", &context)
|
||||
}
|
||||
```
|
||||
|
||||
Templates are rendered with the `render` method. The method takes in the name of
|
||||
a template and a context to render the template with. The context can be any
|
||||
type that implements `Serialize` and serializes into an `Object` value, such as
|
||||
structs, `HashMaps`, and others.
|
||||
|
||||
Rocket searches for a template with the given name in the configurable
|
||||
`template_dir` directory. 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.
|
||||
|
||||
For templates to be properly registered, the template fairing must be attached
|
||||
to the instance of Rocket. Fairings are explained in the next section. To attach
|
||||
the template fairing, simply call `.attach(Template::fairing())` on an instance
|
||||
of `Rocket` as follows:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
rocket::ignite()
|
||||
.mount("/", routes![...])
|
||||
.attach(Template::fairing());
|
||||
}
|
||||
```
|
||||
|
||||
The [`Template`] API
|
||||
documentation contains more information about templates, while the [Handlebars
|
||||
Templates example on
|
||||
GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.8/examples/handlebars_templates)
|
||||
is a fully composed application that makes use of Handlebars templates.
|
||||
|
||||
[`Template`]: https://api.rocket.rs/rocket_contrib/struct.Template.html
|
||||
|
|
Loading…
Reference in New Issue