Document Responder. Further document Flash. Implement Debug for most Responder types.

This commit is contained in:
Sergio Benitez 2016-11-03 15:09:01 +01:00
parent 7beec53889
commit 129268506e
7 changed files with 142 additions and 7 deletions

View File

@ -26,7 +26,7 @@ impl<'a, S, E> IntoOutcome<S, (StatusCode, E), Data> for Result<S, E> {
/// fn submit(var: T) -> ... { ... }
/// ```
///
/// In this example, `T` can be any type that implements `FromData.`
/// In this example, `T` can be any type that implements `FromData`.
///
/// # Outcomes
///

View File

@ -3,6 +3,7 @@ use http::hyper::{header, FreshHyperResponse};
use http::mime::{Mime, TopLevel, SubLevel};
use http::ContentType;
#[derive(Debug)]
pub struct Content<T: Responder>(pub ContentType, pub T);
impl<T: Responder> Responder for Content<T> {
@ -14,6 +15,7 @@ impl<T: Responder> Responder for Content<T> {
macro_rules! impl_data_type_responder {
($name:ident: $top:ident/$sub:ident) => (
#[derive(Debug)]
pub struct $name<T: Responder>(pub T);
impl<T: Responder> Responder for $name<T> {

View File

@ -5,10 +5,11 @@ use response::{self, Responder};
use request::{self, Request, FromRequest};
use http::hyper::{HyperSetCookie, HyperCookiePair, FreshHyperResponse};
// The name of the actual flash cookie.
const FLASH_COOKIE_NAME: &'static str = "_flash";
/// Sets a "flash" cookies that will be removed the next time it is accessed.
/// The anologous type on the request side is
/// Sets a "flash" cookie that will be removed when it is accessed. The
/// anologous request type is
/// [FlashMessage](/rocket/request/type.FlashMessage.html).
///
/// This type makes it easy to send messages across requests. It is typically
@ -30,12 +31,56 @@ const FLASH_COOKIE_NAME: &'static str = "_flash";
/// [FlashMessage](/rocket/request/type.FlashMessage.html) type and the
/// [name](#method.name) and [msg](#method.msg) methods.
///
/// # Responder
/// # Response
///
/// The `Responder` implementation for `Flash` sets the message cookie and then
/// uses the passed in responder `res` to complete the response. In other words,
/// it simply sets a cookie and delagates the rest of the response handling to
/// the wrapped responder.
///
/// # Example
///
/// The following complete Rocket application illustrates the use of a `Flash`
/// message on both the request and response sides.
///
/// ```rust
/// # #![feature(plugin)]
/// # #![plugin(rocket_codegen)]
/// #
/// # extern crate rocket;
/// #
/// use rocket::response::{Flash, Redirect};
/// use rocket::request::FlashMessage;
///
/// #[post("/login/<name>")]
/// fn login(name: &str) -> Result<&'static str, Flash<Redirect>> {
/// if name == "special_user" {
/// Ok("Hello, special user!")
/// } else {
/// Err(Flash::error(Redirect::to("/"), "Invalid username."))
/// }
/// }
///
/// #[get("/")]
/// fn index(flash: Option<FlashMessage>) -> String {
/// flash.map(|msg| format!("{}: {}", msg.name(), msg.msg()))
/// .unwrap_or_else(|| "Welcome!".to_string())
/// }
///
/// fn main() {
/// # if false { // We don't actually want to launch the server in an example.
/// rocket::ignite().mount("/", routes![login, index]).launch()
/// # }
/// }
/// ```
///
/// On the response side (in `login`), a `Flash` error message is set if some
/// fictional authentication failed, and the user is redirected to `"/"`. On the
/// request side (in `index`), the handler emits the flash message if there is
/// one and otherwise emits a standard welcome message. Note that if the user
/// were to refresh the index page after viewing a flash message, the user would
/// receive the standard welcome message.
#[derive(Debug)]
pub struct Flash<R> {
name: String,
message: String,
@ -124,6 +169,10 @@ impl<R: Responder> Flash<R> {
}
}
/// Sets the message cookie and then uses the wrapped responder to complete the
/// response. In other words, simply sets a cookie and delagates the rest of the
/// response handling to the wrapped responder. As a result, the `Outcome` of
/// the response is the `Outcome` of the wrapped `Responder`.
impl<R: Responder> Responder for Flash<R> {
fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> response::Outcome<'b> {
trace_!("Flash: setting message: {}:{}", self.name, self.message);
@ -133,6 +182,7 @@ impl<R: Responder> Responder for Flash<R> {
}
impl Flash<()> {
/// Constructs a new message with the given name and message.
fn named(name: &str, msg: &str) -> Flash<()> {
Flash {
name: name.to_string(),
@ -152,7 +202,11 @@ impl Flash<()> {
}
}
// This is Type-Aliased in Request.
/// Retrieves a flash message from a flash cookie and deletes the flash cookie.
/// If there is no flash cookie, an empty `Err` is returned.
///
/// The suggested use is through an `Option` and the `FlashMessage` type alias
/// in `request`: `Option<FlashMessage>`.
impl<'r> FromRequest<'r> for Flash<()> {
type Error = ();

View File

@ -1,8 +1,17 @@
//! Types and traits to build and send responses.
//!
//! The return type of a Rocket handler can be any type that implements the
//! [Responder](trait.Responder.html) trait. This module contains several useful
//! types that implement this trait.
//! [Responder](trait.Responder.html) trait. This module contains several such
//! types.
//!
//! # Composing
//!
//! Many of the built-in `Responder` types _chain_ responses: they take in
//! another `Responder` and simply add, remove, or change information in the
//! response. In other words, many `Responder` types are built to compose well.
//! As a result, you'll often have types of the form `A<B<C>>` consisting of
//! three `Responder`s `A`, `B`, and `C`. This is normal and encouraged as the
//! type names typically illustrate the intended response.
mod responder;
mod redirect;

View File

@ -19,7 +19,69 @@ impl<'a, T, E> IntoOutcome<(), (), (StatusCode, FreshHyperResponse<'a>)> for Res
}
}
/// Trait implemented by types that send a response to clients.
///
/// Types that implement this trait can be used as the return type of a handler,
/// as illustrated below:
///
/// ```rust,ignore
/// #[get("/")]
/// fn index() -> T { ... }
/// ```
///
/// In this example, `T` can be any type that implements `Responder`.
///
/// # Outcomes
///
/// The returned [Outcome](/rocket/outcome/index.html) of a `respond` call
/// determines how the response will be processed, if at all.
///
/// * **Success**
///
/// An `Outcome` of `Success` indicates that the responder was successful in
/// sending the response to the client. No further processing will occur as a
/// result.
///
/// * **Failure**
///
/// An `Outcome` of `Failure` indicates that the responder failed after
/// beginning a response. The response is incomplete, and there is no way to
/// salvage the response. No further processing will occur.
///
/// * **Forward**(StatusCode, FreshHyperResponse<'a>)
///
/// If the `Outcome` is `Forward`, the response will be forwarded to the
/// designated error [Catcher](/rocket/struct.Catcher.html) for the given
/// `StatusCode`. This requires that a response wasn't started and thus is
/// still fresh.
///
/// # Implementation Tips
///
/// This section describes a few best practices to take into account when
/// implementing `Responder`.
///
/// ## Debug
///
/// A type implementing `Responder` should implement the `Debug` trait when
/// possible. This is because the `Responder` implementation for `Result`
/// requires its `Err` type to implement `Debug`. Therefore, a type implementing
/// `Debug` can more easily be composed.
///
/// ## Check Before Changing
///
/// Unless a given type is explicitly designed to change some information in
/// ther esponse, it should first _check_ that some information hasn't been set
/// before _changing_ that information. For example, before setting the
/// `Content-Type` header of a response, first check that the header hasn't been
/// set.
pub trait Responder {
/// Attempts to write a response to `res`.
///
/// If writing the response successfully completes, an outcome of `Success`
/// is returned. If writing the response begins but fails, an outcome of
/// `Failure` is returned. If writing a response fails before writing
/// anything out, an outcome of `Forward` can be returned, which causes the
/// response to be written by the appropriate error catcher instead.
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a>;
}

View File

@ -1,4 +1,5 @@
use std::io::{Read, Write, ErrorKind};
use std::fmt::{self, Debug};
use response::{Responder, Outcome};
use http::hyper::FreshHyperResponse;
@ -26,6 +27,12 @@ impl<T: Read> Stream<T> {
// }
}
impl<T: Read + Debug> Debug for Stream<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Stream({:?})", self.0)
}
}
impl<T: Read> Responder for Stream<T> {
fn respond<'a>(&mut self, res: FreshHyperResponse<'a>) -> Outcome<'a> {
let mut stream = match res.start() {

View File

@ -1,6 +1,7 @@
use response::{Responder, Outcome};
use http::hyper::{StatusCode, FreshHyperResponse};
#[derive(Debug)]
pub struct StatusResponse<R: Responder> {
status: StatusCode,
responder: R,