6.6 KiB
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 trait can be returned, including your own.
Responder
Types that implement Responder
know how to generate a
Response 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 and
Flash, which you can
find in the response module.
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.
Wrapping
Responders can wrap other responders. That is, responders can be of the
following form, where R: Responder
:
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. 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>
:
#[get("/")]
fn accept() -> status::Accepted<String> {
status::Accepted(Some("I accept!".to_string()))
}
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
module 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
type as follows:
#[get("/")]
fn json() -> content::JSON<&'static str> {
content::JSON("{ 'hi': 'world' }")
}
Result
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
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
the request to the error catcher for the
given status code.
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
While not encouraged, you can also forward a request to a catcher manually by using the Failure type. For instance, to forward to the catcher for 406 Not Acceptable, you would write:
#[get("/")]
fn just_fail() -> Failure {
Failure(Status::NotAcceptable)
}
JSON
Responding with JSON data is simple: return a value of type
JSON. For example, to
respond with the JSON value of the Task
structure from previous examples, we
would write:
#[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.
Templates
Rocket has built-in support for templating. To respond with a rendered template, simply return a Template type.
#[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. 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.
The context can be any type that implements Serialize
and serializes to an
Object
value, such as structs, HashMaps
, and others. The
Template API
documentation contains more information about templates, while the Handlebars
Templates example on
GitHub
is a fully composed application that makes use of Handlebars templates.
Streaming
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
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:
#[get("/stream")]
fn stream() -> io::Result<Stream<UnixStream>> {
UnixStream::connect("/path/to/my/socket").map(|s| Stream::from(s))
}
Rocket takes care of the rest.