Rocket/site/guide/responses.md

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.