mirror of
https://github.com/rwf2/Rocket.git
synced 2025-02-07 09:12:09 +00:00
Improve 'Responder' API docs.
The improvements are: * Point directly and immediately to the 'Responder' derive. * Provide more discussion on lifetimes. * Format documentation for easier scanning.
This commit is contained in:
parent
d34195fe11
commit
770f332832
@ -7,29 +7,47 @@ use crate::request::Request;
|
|||||||
|
|
||||||
/// Trait implemented by types that generate responses for clients.
|
/// Trait implemented by types that generate responses for clients.
|
||||||
///
|
///
|
||||||
/// Types that implement this trait can be used as the return type of a handler,
|
/// Any type that implements `Responder` can be used as the return type of a
|
||||||
/// as illustrated below with `T`:
|
/// handler:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # #[macro_use] extern crate rocket;
|
/// # #[macro_use] extern crate rocket;
|
||||||
/// # type T = ();
|
/// # type T = ();
|
||||||
/// #
|
/// #
|
||||||
|
/// // This works for any `T` that implements `Responder`.
|
||||||
/// #[get("/")]
|
/// #[get("/")]
|
||||||
/// fn index() -> T { /* ... */ }
|
/// fn index() -> T { /* ... */ }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// In this example, `T` can be any type, as long as it implements `Responder`.
|
/// # Deriving
|
||||||
///
|
///
|
||||||
/// # Return Value
|
/// This trait can, and largely _should_, be automatically derived. The derive
|
||||||
|
/// can handle all simple cases and most complex cases, too. When deriving
|
||||||
|
/// `Responder`, the first field of the annotated structure (or of each variant
|
||||||
|
/// if an `enum`) is used to generate a response while the remaining fields are
|
||||||
|
/// used as response headers:
|
||||||
///
|
///
|
||||||
/// A `Responder` returns an `Ok(Response)` or an `Err(Status)`:
|
/// ```rust
|
||||||
|
/// # #[macro_use] extern crate rocket;
|
||||||
|
/// # #[cfg(feature = "json")] mod _main {
|
||||||
|
/// # type Template = String;
|
||||||
|
/// use rocket::http::ContentType;
|
||||||
|
/// use rocket::serde::{Serialize, json::Json};
|
||||||
///
|
///
|
||||||
/// * An `Ok` variant means that the `Responder` was successful in generating
|
/// #[derive(Responder)]
|
||||||
/// a `Response`. The `Response` will be written out to the client.
|
/// #[response(bound = "T: Serialize")]
|
||||||
|
/// enum Error<T> {
|
||||||
|
/// #[response(status = 400)]
|
||||||
|
/// Unauthorized(Json<T>),
|
||||||
|
/// #[response(status = 404)]
|
||||||
|
/// NotFound(Template, ContentType),
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
///
|
///
|
||||||
/// * An `Err` variant means that the `Responder` could not or did not
|
/// For full details on deriving `Responder`, see the [`Responder` derive].
|
||||||
/// generate a `Response`. The contained `Status` will be used to find the
|
///
|
||||||
/// relevant error catcher which then generates an error response.
|
/// [`Responder` derive]: derive@crate::Responder
|
||||||
///
|
///
|
||||||
/// # Provided Implementations
|
/// # Provided Implementations
|
||||||
///
|
///
|
||||||
@ -86,37 +104,97 @@ use crate::request::Request;
|
|||||||
/// to the client. If the `Result` is `Err`, the wrapped `Err` responder is
|
/// to the client. If the `Result` is `Err`, the wrapped `Err` responder is
|
||||||
/// used to respond to the client.
|
/// used to respond to the client.
|
||||||
///
|
///
|
||||||
|
/// # Return Value
|
||||||
|
///
|
||||||
|
/// A `Responder` returns a `Future` whose output type is a `Result<Response,
|
||||||
|
/// Status>`.
|
||||||
|
///
|
||||||
|
/// * An `Ok(Response)` indicates success. The `Response` will be written out
|
||||||
|
/// to the client.
|
||||||
|
///
|
||||||
|
/// * An `Err(Status)` indicates failure. The error catcher for `Status` will
|
||||||
|
/// be invoked to generate a response.
|
||||||
|
///
|
||||||
/// # Implementation Tips
|
/// # Implementation Tips
|
||||||
///
|
///
|
||||||
/// This section describes a few best practices to take into account when
|
/// This section describes a few best practices to take into account when
|
||||||
/// implementing `Responder`.
|
/// implementing `Responder`.
|
||||||
///
|
///
|
||||||
/// ## Joining and Merging
|
/// 1. Avoid Manual Implementations
|
||||||
///
|
///
|
||||||
/// When chaining/wrapping other `Responder`s, use the
|
/// The [`Responder` derive] is a powerful mechanism that eliminates the need
|
||||||
/// [`merge()`](Response::merge()) or [`join()`](Response::join()) methods on
|
/// to implement `Responder` in almost all cases. We encourage you to explore
|
||||||
/// the `Response` or `ResponseBuilder` struct. Ensure that you document the
|
/// using the derive _before_ attempting to implement `Responder` directly.
|
||||||
/// merging or joining behavior appropriately.
|
/// It allows you to leverage existing `Responder` implementations through
|
||||||
|
/// composition, decreasing the opportunity for mistakes or performance
|
||||||
|
/// degradation.
|
||||||
///
|
///
|
||||||
/// ## Inspecting Requests
|
/// 2. Joining and Merging
|
||||||
///
|
///
|
||||||
/// A `Responder` has access to the request it is responding to. Even so, you
|
/// When chaining/wrapping other `Responder`s, start with
|
||||||
/// should avoid using the `Request` value as much as possible. This is because
|
/// [`Response::build_from()`] and/or use the [`merge()`](Response::merge())
|
||||||
/// using the `Request` object makes your responder _impure_, and so the use of
|
/// or [`join()`](Response::join()) methods on the `Response` or
|
||||||
/// the type as a `Responder` has less intrinsic meaning associated with it. If
|
/// `ResponseBuilder` struct. Ensure that you document merging or joining
|
||||||
/// the `Responder` were pure, however, it would always respond in the same manner,
|
/// behavior appropriatse.
|
||||||
/// regardless of the incoming request. Thus, knowing the type is sufficient to
|
///
|
||||||
/// fully determine its functionality.
|
/// 3. Inspecting Requests
|
||||||
|
///
|
||||||
|
/// While tempting, a `Responder` that varies its functionality based on the
|
||||||
|
/// incoming request sacrifices its functionality being understood based
|
||||||
|
/// purely on its type. By implication, gleaming the functionality of a
|
||||||
|
/// _handler_ from its type signature also becomes more difficult. You should
|
||||||
|
/// avoid varying responses based on the `Request` value as much as possible.
|
||||||
///
|
///
|
||||||
/// ## Lifetimes
|
/// ## Lifetimes
|
||||||
///
|
///
|
||||||
/// `Responder` has two lifetimes: `Responder<'r, 'o: 'r>`. The first lifetime,
|
/// `Responder` has two lifetimes: `Responder<'r, 'o: 'r>`.
|
||||||
/// `'r`, refers to the reference to the `&'r Request`, while the second
|
///
|
||||||
/// lifetime refers to the returned `Response<'o>`. The bound `'o: 'r` allows
|
/// * `'r` bounds the reference to the `&'r Request`.
|
||||||
/// `'o` to be any lifetime that lives at least as long as the `Request`. In
|
///
|
||||||
/// particular, this includes borrows from the `Request` itself (where `'o` would
|
/// * `'o` bounds the returned `Response<'o>` to values that live at least as
|
||||||
/// be `'r` as in `impl<'r> Responder<'r, 'r>`) as well as `'static` data (where
|
/// long as the request.
|
||||||
/// `'o` would be `'static` as in `impl<'r> Responder<'r, 'static>`).
|
///
|
||||||
|
/// This includes borrows from the `Request` itself (where `'o` would be
|
||||||
|
/// `'r` as in `impl<'r> Responder<'r, 'r>`) as well as `'static` data
|
||||||
|
/// (where `'o` would be `'static` as in `impl<'r> Responder<'r, 'static>`).
|
||||||
|
///
|
||||||
|
/// In practice, you are likely choosing between four signatures:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use rocket::request::Request;
|
||||||
|
/// # use rocket::response::{self, Responder};
|
||||||
|
/// # struct A;
|
||||||
|
/// // If the response contains no borrowed data.
|
||||||
|
/// impl<'r> Responder<'r, 'static> for A {
|
||||||
|
/// fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # struct B<'r>(&'r str);
|
||||||
|
/// // If the response borrows from the request.
|
||||||
|
/// impl<'r> Responder<'r, 'r> for B<'r> {
|
||||||
|
/// fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # struct C;
|
||||||
|
/// // If the response is or wraps a borrow that may outlive the request.
|
||||||
|
/// impl<'r, 'o: 'r> Responder<'r, 'o> for &'o C {
|
||||||
|
/// fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # struct D<R>(R);
|
||||||
|
/// // If the response wraps an existing responder.
|
||||||
|
/// impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for D<R> {
|
||||||
|
/// fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -162,10 +240,9 @@ use crate::request::Request;
|
|||||||
/// use rocket::http::ContentType;
|
/// use rocket::http::ContentType;
|
||||||
///
|
///
|
||||||
/// impl<'r> Responder<'r, 'static> for Person {
|
/// impl<'r> Responder<'r, 'static> for Person {
|
||||||
/// fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
|
/// fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
|
||||||
/// let person_string = format!("{}:{}", self.name, self.age);
|
/// let string = format!("{}:{}", self.name, self.age);
|
||||||
/// Response::build()
|
/// Response::build_from(string.respond_to(req)?)
|
||||||
/// .sized_body(person_string.len(), Cursor::new(person_string))
|
|
||||||
/// .raw_header("X-Person-Name", self.name)
|
/// .raw_header("X-Person-Name", self.name)
|
||||||
/// .raw_header("X-Person-Age", self.age.to_string())
|
/// .raw_header("X-Person-Age", self.age.to_string())
|
||||||
/// .header(ContentType::new("application", "x-person"))
|
/// .header(ContentType::new("application", "x-person"))
|
||||||
@ -177,6 +254,35 @@ use crate::request::Request;
|
|||||||
/// # fn person() -> Person { Person { name: "a".to_string(), age: 20 } }
|
/// # fn person() -> Person { Person { name: "a".to_string(), age: 20 } }
|
||||||
/// # fn main() { }
|
/// # fn main() { }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// Note that the implementation could have instead been derived if structured
|
||||||
|
/// in a slightly different manner:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::http::Header;
|
||||||
|
/// use rocket::response::Responder;
|
||||||
|
///
|
||||||
|
/// #[derive(Responder)]
|
||||||
|
/// #[response(content_type = "application/x-person")]
|
||||||
|
/// struct Person {
|
||||||
|
/// text: String,
|
||||||
|
/// name: Header<'static>,
|
||||||
|
/// age: Header<'static>,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl Person {
|
||||||
|
/// fn new(name: &str, age: usize) -> Person {
|
||||||
|
/// Person {
|
||||||
|
/// text: format!("{}:{}", name, age),
|
||||||
|
/// name: Header::new("X-Person-Name", name.to_string()),
|
||||||
|
/// age: Header::new("X-Person-Age", age.to_string())
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// #
|
||||||
|
/// # #[rocket::get("/person")]
|
||||||
|
/// # fn person() -> Person { Person::new("Bob", 29) }
|
||||||
|
/// ```
|
||||||
pub trait Responder<'r, 'o: 'r> {
|
pub trait Responder<'r, 'o: 'r> {
|
||||||
/// Returns `Ok` if a `Response` could be generated successfully. Otherwise,
|
/// Returns `Ok` if a `Response` could be generated successfully. Otherwise,
|
||||||
/// returns an `Err` with a failing `Status`.
|
/// returns an `Err` with a failing `Status`.
|
||||||
|
Loading…
Reference in New Issue
Block a user