Prefix 'content' responder names with 'Raw'.

The primary aim of this commit is to reduce confusion between
'content::Json' and 'rocket::serde::json::Json' be renaming the former
to 'content::RawJson'. The complete changes in this PR are:

  * All responders in the 'content' module are prefixed with 'Raw'.
  * The 'content::Custom' responder was removed entirely.
  * The 'Plain' responder is now 'RawText'.
  * The 'content' API docs point to the 'serde' responders.
  * The docs and examples were updated accordingly.
This commit is contained in:
Sergio Benitez 2021-07-20 02:06:17 -07:00
parent 179be25866
commit cc0621626b
12 changed files with 76 additions and 100 deletions

View File

@ -8,7 +8,8 @@ use crate::request::Request;
use crate::response::{self, Responder};
use crate::http::ContentType;
/// A [`Responder`] that sends a file with a Content-Type based on its name.
/// A [`Responder`] that sends file data with a Content-Type based on its
/// file extension.
///
/// # Example
///

View File

@ -2,15 +2,11 @@
//!
//! # Usage
//!
//! Each type wraps a given responder. The `Responder` implementation of each
//! type replaces the Content-Type of the wrapped responder and delegates the
//! remainder of the response to the wrapped responder. This allows for setting
//! the Content-Type of a type that doesn't set it itself or for overriding one
//! that does.
//!
//! The [`Custom`] type allows responding with _any_ `Content-Type`. As a
//! convenience, `(ContentType, R)` where `R: Responder` is _also_ a
//! `Responder`, identical to `Custom`.
//! Each type in this module is a `Responder` that wraps an existing
//! `Responder`, overwriting the `Content-Type` of the response but otherwise
//! delegating the response to the wrapped responder. As a convenience,
//! `(ContentType, R)` where `R: Responder` is _also_ a `Responder` that
//! overrides the `Content-Type` to the value in `.0`:
//!
//! ```rust
//! # use rocket::get;
@ -21,55 +17,24 @@
//! (ContentType::HTML, "Is this HTML? <p>Sure, why not!</p>")
//! }
//! ```
//!
//! # Example
//!
//! The following snippet creates an `Html` content response for a string.
//! Normally, raw strings set their response Content-Type to `text/plain`. By
//! using the `Html` content response, the Content-Type will be set to
//! `text/html` instead.
//! The following snippet creates a `RawHtml` response from a string. Normally,
//! raw strings set their response Content-Type to `text/plain`. By using the
//! `RawHtml` content response, the Content-Type will be set to `text/html`
//! instead:
//!
//! ```rust
//! use rocket::response::content;
//!
//! # #[allow(unused_variables)]
//! let response = content::Html("<h1>Hello, world!</h1>");
//! let response = content::RawHtml("<h1>Hello, world!</h1>");
//! ```
use crate::request::Request;
use crate::response::{self, Response, Responder};
use crate::http::ContentType;
/// Sets the Content-Type of a `Responder` to a chosen value.
///
/// Delegates the remainder of the response to the wrapped responder.
///
/// # Example
///
/// Set the Content-Type of a string to PDF.
///
/// ```rust
/// use rocket::response::content::Custom;
/// use rocket::http::ContentType;
///
/// # #[allow(unused_variables)]
/// let response = Custom(ContentType::PDF, "Hi.");
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct Custom<R>(pub ContentType, pub R);
/// Overrides the Content-Type of the response to the wrapped `ContentType` then
/// delegates the remainder of the response to the wrapped responder.
impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Custom<R> {
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> {
Response::build()
.merge(self.1.respond_to(req)?)
.header(self.0)
.ok()
}
}
macro_rules! ctrs {
($($name:ident: $ct:ident, $name_str:expr, $ct_str:expr),+) => {
$(
@ -80,6 +45,13 @@ macro_rules! ctrs {
#[doc="</i>."]
///
/// Delegates the remainder of the response to the wrapped responder.
///
/// **Note:** Unlike types like [`Json`](crate::serde::json::Json)
/// and [`MsgPack`](crate::serde::msgpack::MsgPack), this type _does
/// not_ serialize data in any way. You should _always_ use those
/// types to respond with serializable data. Additionally, you
/// should _always_ use [`NamedFile`](crate::fs::NamedFile), which
/// automatically sets a `Content-Type`, to respond with file data.
#[derive(Debug, Clone, PartialEq)]
pub struct $name<R>(pub R);
@ -87,7 +59,7 @@ macro_rules! ctrs {
/// remainder of the response to the wrapped responder.
impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for $name<R> {
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> {
Custom(ContentType::$ct, self.0).respond_to(req)
(ContentType::$ct, self.0).respond_to(req)
}
}
)+
@ -96,18 +68,20 @@ macro_rules! ctrs {
ctrs! {
// FIXME: Add a note that this is _not_ `serde::Json`.
Json: JSON, "JSON", "application/json",
Xml: XML, "XML", "text/xml",
MsgPack: MsgPack, "MessagePack", "application/msgpack",
Html: HTML, "HTML", "text/html",
Plain: Plain, "plain text", "text/plain",
Css: CSS, "CSS", "text/css",
JavaScript: JavaScript, "JavaScript", "application/javascript"
RawJson: JSON, "JSON", "application/json",
RawXml: XML, "XML", "text/xml",
RawMsgPack: MsgPack, "MessagePack", "application/msgpack",
RawHtml: HTML, "HTML", "text/html",
RawText: Text, "plain text", "text/plain",
RawCss: CSS, "CSS", "text/css",
RawJavaScript: JavaScript, "JavaScript", "application/javascript"
}
impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for (ContentType, R) {
#[inline(always)]
fn respond_to(self, request: &'r Request<'_>) -> response::Result<'o> {
Custom(self.0, self.1).respond_to(request)
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> {
Response::build()
.merge(self.1.respond_to(req)?)
.header(self.0)
.ok()
}
}

View File

@ -223,7 +223,7 @@ impl<'r, T: Serialize> Responder<'r, 'static> for Json<T> {
Status::InternalServerError
})?;
content::Json(string).respond_to(req)
content::RawJson(string).respond_to(req)
}
}
@ -299,7 +299,7 @@ impl<'v, T: Deserialize<'v> + Send> form::FromFormField<'v> for Json<T> {
/// and a fixed-size body with the serialized value.
impl<'r> Responder<'r, 'static> for Value {
fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
content::Json(self.to_string()).respond_to(req)
content::RawJson(self.to_string()).respond_to(req)
}
}

View File

@ -194,7 +194,7 @@ impl<'r, T: Serialize> Responder<'r, 'static> for MsgPack<T> {
Status::InternalServerError
})?;
content::MsgPack(buf).respond_to(req)
content::RawMsgPack(buf).respond_to(req)
}
}

View File

@ -1,6 +1,7 @@
#[macro_use] extern crate rocket;
use rocket::{http::Status, response::content};
use rocket::http::Status;
use rocket::response::content::RawJson;
#[get("/empty")]
fn empty() -> Status {
@ -13,8 +14,8 @@ fn index() -> &'static str {
}
#[head("/other")]
fn other() -> content::Json<&'static str> {
content::Json("{ 'hi': 'hello' }")
fn other() -> RawJson<&'static str> {
RawJson("{ 'hi': 'hello' }")
}
mod head_handling_tests {

View File

@ -5,12 +5,12 @@
mod session;
mod message;
use rocket::response::content::Html;
use rocket::response::content::RawHtml;
use rocket_dyn_templates::Template;
#[get("/")]
fn index() -> Html<&'static str> {
Html(r#"<a href="message">Set a Message</a> or <a href="session">Use Sessions</a>."#)
fn index() -> RawHtml<&'static str> {
RawHtml(r#"<a href="message">Set a Message</a> or <a href="session">Use Sessions</a>."#)
}
#[launch]

View File

@ -17,16 +17,16 @@ fn forced_error(code: u16) -> Status {
}
#[catch(404)]
fn general_not_found() -> content::Html<&'static str> {
content::Html(r#"
fn general_not_found() -> content::RawHtml<&'static str> {
content::RawHtml(r#"
<p>Hmm... What are you looking for?</p>
Say <a href="/hello/Sergio/100">hello!</a>
"#)
}
#[catch(404)]
fn hello_not_found(req: &Request<'_>) -> content::Html<String> {
content::Html(format!("\
fn hello_not_found(req: &Request<'_>) -> content::RawHtml<String> {
content::RawHtml(format!("\
<p>Sorry, but '{}' is not a valid path!</p>\
<p>Try visiting /hello/&lt;name&gt;/&lt;age&gt; instead.</p>",
req.uri()))

View File

@ -7,7 +7,7 @@ use std::io;
use rocket::data::{Data, ToByteUnit};
use rocket::http::uri::Absolute;
use rocket::response::content::Plain;
use rocket::response::content::RawText;
use rocket::tokio::fs::{self, File};
use crate::paste_id::PasteId;
@ -24,8 +24,8 @@ async fn upload(paste: Data<'_>) -> io::Result<String> {
}
#[get("/<id>")]
async fn retrieve(id: PasteId<'_>) -> Option<Plain<File>> {
File::open(id.file_path()).await.map(Plain).ok()
async fn retrieve(id: PasteId<'_>) -> Option<RawText<File>> {
File::open(id.file_path()).await.map(RawText).ok()
}
#[delete("/<id>")]

View File

@ -89,26 +89,26 @@ fn maybe_redir(name: &str) -> Result<&'static str, Redirect> {
use rocket::Request;
use rocket::response::content;
// NOTE: This example explicitly uses the `Json` type from `response::content`
// for demonstration purposes. In a real application, _always_ prefer to use
// `rocket::serde::json::Json` instead!
// NOTE: This example explicitly uses the `RawJson` type from
// `response::content` for demonstration purposes. In a real application,
// _always_ prefer to use `rocket::serde::json::Json` instead!
// In a `GET` request and all other non-payload supporting request types, the
// preferred media type in the Accept header is matched against the `format` in
// the route attribute. Because the client can use non-specific media types like
// `*/*` in `Accept`, these first two routes would collide without `rank`.
#[get("/content", format = "xml", rank = 1)]
fn xml() -> content::Xml<&'static str> {
content::Xml("<payload>I'm here</payload>")
fn xml() -> content::RawXml<&'static str> {
content::RawXml("<payload>I'm here</payload>")
}
#[get("/content", format = "json", rank = 2)]
fn json() -> content::Json<&'static str> {
content::Json(r#"{ "payload": "I'm here" }"#)
fn json() -> content::RawJson<&'static str> {
content::RawJson(r#"{ "payload": "I'm here" }"#)
}
#[catch(404)]
fn not_found(request: &Request<'_>) -> content::Html<String> {
fn not_found(request: &Request<'_>) -> content::RawHtml<String> {
let html = match request.format() {
Some(ref mt) if !(mt.is_xml() || mt.is_html()) => {
format!("<p>'{}' requests are not supported.</p>", mt)
@ -118,24 +118,24 @@ fn not_found(request: &Request<'_>) -> content::Html<String> {
request.uri())
};
content::Html(html)
content::RawHtml(html)
}
/******************************* `Either` Responder ***************************/
use rocket::Either;
use rocket::response::content::{Json, MsgPack};
use rocket::response::content::{RawJson, RawMsgPack};
use rocket::http::uncased::AsUncased;
// NOTE: In a real application, we'd use `Json` and `MsgPack` from
// `rocket::serde`, which perform automatic serialization of responses and
// automatically set the `Content-Type`.
#[get("/content/<kind>")]
fn json_or_msgpack(kind: &str) -> Either<Json<&'static str>, MsgPack<&'static [u8]>> {
fn json_or_msgpack(kind: &str) -> Either<RawJson<&'static str>, RawMsgPack<&'static [u8]>> {
if kind.as_uncased() == "msgpack" {
Either::Right(MsgPack(&[162, 104, 105]))
Either::Right(RawMsgPack(&[162, 104, 105]))
} else {
Either::Left(Json("\"hi\""))
Either::Left(RawJson("\"hi\""))
}
}
@ -143,7 +143,7 @@ fn json_or_msgpack(kind: &str) -> Either<Json<&'static str>, MsgPack<&'static [u
use std::borrow::Cow;
use rocket::response::content::Html;
use rocket::response::content::RawHtml;
#[derive(Responder)]
enum StoredData {
@ -151,7 +151,7 @@ enum StoredData {
String(Cow<'static, str>),
Bytes(Vec<u8>),
#[response(status = 401)]
NotAuthorized(Html<&'static str>),
NotAuthorized(RawHtml<&'static str>),
}
#[derive(FromFormField, UriDisplayQuery)]
@ -170,7 +170,7 @@ async fn custom(kind: Option<Kind>) -> StoredData {
},
Some(Kind::String) => StoredData::String("Hey, I'm some data.".into()),
Some(Kind::Bytes) => StoredData::Bytes(vec![72, 105]),
None => StoredData::NotAuthorized(Html("No no no!"))
None => StoredData::NotAuthorized(RawHtml("No no no!"))
}
}

View File

@ -1,15 +1,15 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use rocket::State;
use rocket::response::content;
use rocket::response::content::RawHtml;
use rocket::fairing::AdHoc;
struct HitCount(AtomicUsize);
#[get("/")]
fn index(hit_count: &State<HitCount>) -> content::Html<String> {
fn index(hit_count: &State<HitCount>) -> RawHtml<String> {
let count = hit_count.0.fetch_add(1, Ordering::Relaxed) + 1;
content::Html(format!("Your visit is recorded!<br /><br />Visits: {}", count))
RawHtml(format!("Your visit is recorded!<br /><br />Visits: {}", count))
}
pub fn stage() -> AdHoc {

View File

@ -5,12 +5,12 @@ mod tera;
#[cfg(test)] mod tests;
use rocket::response::content::Html;
use rocket::response::content::RawHtml;
use rocket_dyn_templates::Template;
#[get("/")]
fn index() -> Html<&'static str> {
Html(r#"See <a href="tera">Tera</a> or <a href="hbs">Handlebars</a>."#)
fn index() -> RawHtml<&'static str> {
RawHtml(r#"See <a href="tera">Tera</a> or <a href="hbs">Handlebars</a>."#)
}
#[launch]

View File

@ -50,7 +50,7 @@ fn new(id: usize) -> status::Accepted<String> {
Similarly, the types in the [`content` module](@api/rocket/response/content/)
can be used to override the Content-Type of a response. For instance, to set the
Content-Type of `&'static str` to JSON, as well as setting the status code to an
arbitrary one like `418 I'm a teapot`, combine [`content::Json`] with
arbitrary one like `418 I'm a teapot`, combine [`content::RawJson`] with
[`status::Custom`]:
```rust
@ -59,16 +59,16 @@ use rocket::http::Status;
use rocket::response::{content, status};
#[get("/")]
fn json() -> status::Custom<content::Json<&'static str>> {
status::Custom(Status::ImATeapot, content::Json("{ \"hi\": \"world\" }"))
fn json() -> status::Custom<content::RawJson<&'static str>> {
status::Custom(Status::ImATeapot, content::RawJson("{ \"hi\": \"world\" }"))
}
```
! warning: This is _not_ the same as [`serde::json::Json`]!
The built-in `(Status, R)` and `(ContentType, R)` responders, where `R:
Responder`, are short-hands for the `status::Custom` and `content::Custom`
responders:
Responder`, also override the `Status` and `Content-Type` of responses,
respectively:
```rust
# #[macro_use] extern crate rocket;