rocket_community/response/
redirect.rs

1use crate::http::uri::Reference;
2use crate::http::Status;
3use crate::request::Request;
4use crate::response::{self, Responder, Response};
5
6/// An empty redirect response to a given URL.
7///
8/// This type simplifies returning a redirect response to the client.
9///
10/// # Usage
11///
12/// All constructors accept a generic type of `T: TryInto<Reference<'static>>`.
13/// Among the candidate types are:
14///
15///   * `String`, `&'static str`
16///   * [`Origin`](crate::http::uri::Origin)
17///   * [`Authority`](crate::http::uri::Authority)
18///   * [`Absolute`](crate::http::uri::Absolute)
19///   * [`Reference`](crate::http::uri::Reference)
20///
21/// Any non-`'static` strings must first be allocated using `.to_string()` or
22/// similar before being passed to a `Redirect` constructor. When redirecting to
23/// a route, or any URI containing a route, _always_ use [`uri!`] to construct a
24/// valid URI:
25///
26/// ```rust
27/// # #[macro_use] extern crate rocket_community as rocket;
28/// use rocket::response::Redirect;
29///
30/// #[get("/hello/<name>/<age>")]
31/// fn hello(name: String, age: u8) -> String {
32///     format!("Hello, {} year old named {}!", age, name)
33/// }
34///
35/// #[get("/hi/<name>/<age>")]
36/// fn hi(name: String, age: u8) -> Redirect {
37///     Redirect::to(uri!(hello(name, age)))
38/// }
39///
40/// #[get("/bye/<name>/<age>")]
41/// fn bye(name: String, age: u8) -> Redirect {
42///     Redirect::to(uri!("https://rocket.rs/bye", hello(name, age), "?bye#now"))
43/// }
44/// ```
45///
46/// [`Origin`]: crate::http::uri::Origin
47/// [`uri!`]: ../macro.uri.html
48#[derive(Debug, Clone)]
49pub struct Redirect(Status, Option<Reference<'static>>);
50
51impl Redirect {
52    /// Construct a temporary "see other" (303) redirect response. This is the
53    /// typical response when redirecting a user to another page. This type of
54    /// redirect indicates that the client should look elsewhere, but always via
55    /// a `GET` request, for a given resource.
56    ///
57    /// # Examples
58    ///
59    /// ```rust
60    /// # #[macro_use] extern crate rocket_community as rocket;
61    /// use rocket::response::Redirect;
62    ///
63    /// let redirect = Redirect::to(uri!("/foo/bar"));
64    /// let redirect = Redirect::to(uri!("https://domain.com#foo"));
65    /// ```
66    pub fn to<U: TryInto<Reference<'static>>>(uri: U) -> Redirect {
67        Redirect(Status::SeeOther, uri.try_into().ok())
68    }
69
70    /// Construct a "temporary" (307) redirect response. This response instructs
71    /// the client to reissue the current request to a different URL,
72    /// maintaining the contents of the request identically. This means that,
73    /// for example, a `POST` request will be resent, contents included, to the
74    /// requested URL.
75    ///
76    /// # Examples
77    ///
78    /// ```rust
79    /// # #[macro_use] extern crate rocket_community as rocket;
80    /// use rocket::response::Redirect;
81    ///
82    /// let redirect = Redirect::temporary(uri!("some/other/path"));
83    /// let redirect = Redirect::temporary(uri!("https://rocket.rs?foo"));
84    /// let redirect = Redirect::temporary(format!("some-{}-thing", "crazy"));
85    /// ```
86    pub fn temporary<U: TryInto<Reference<'static>>>(uri: U) -> Redirect {
87        Redirect(Status::TemporaryRedirect, uri.try_into().ok())
88    }
89
90    /// Construct a "permanent" (308) redirect response. This redirect must only
91    /// be used for permanent redirects as it is cached by clients. This
92    /// response instructs the client to reissue requests for the current URL to
93    /// a different URL, now and in the future, maintaining the contents of the
94    /// request identically. This means that, for example, a `POST` request will
95    /// be resent, contents included, to the requested URL.
96    ///
97    /// # Examples
98    ///
99    /// ```rust
100    /// # #[macro_use] extern crate rocket_community as rocket;
101    /// use rocket::response::Redirect;
102    ///
103    /// let redirect = Redirect::permanent(uri!("/other_url"));
104    /// let redirect = Redirect::permanent(format!("some-{}-thing", "crazy"));
105    /// ```
106    pub fn permanent<U: TryInto<Reference<'static>>>(uri: U) -> Redirect {
107        Redirect(Status::PermanentRedirect, uri.try_into().ok())
108    }
109
110    /// Construct a temporary "found" (302) redirect response. This response
111    /// instructs the client to reissue the current request to a different URL,
112    /// ideally maintaining the contents of the request identically.
113    /// Unfortunately, different clients may respond differently to this type of
114    /// redirect, so `303` or `307` redirects, which disambiguate, are
115    /// preferred.
116    ///
117    /// # Examples
118    ///
119    /// ```rust
120    /// # #[macro_use] extern crate rocket_community as rocket;
121    /// use rocket::response::Redirect;
122    ///
123    /// let redirect = Redirect::found(uri!("/other_url"));
124    /// let redirect = Redirect::found(format!("some-{}-thing", "crazy"));
125    /// ```
126    pub fn found<U: TryInto<Reference<'static>>>(uri: U) -> Redirect {
127        Redirect(Status::Found, uri.try_into().ok())
128    }
129
130    /// Construct a permanent "moved" (301) redirect response. This response
131    /// should only be used for permanent redirects as it can be cached by
132    /// browsers. Because different clients may respond differently to this type
133    /// of redirect, a `308` redirect, which disambiguates, is preferred.
134    ///
135    /// # Examples
136    ///
137    /// ```rust
138    /// # #[macro_use] extern crate rocket_community as rocket;
139    /// use rocket::response::Redirect;
140    ///
141    /// let redirect = Redirect::moved(uri!("here"));
142    /// let redirect = Redirect::moved(format!("some-{}-thing", "crazy"));
143    /// ```
144    pub fn moved<U: TryInto<Reference<'static>>>(uri: U) -> Redirect {
145        Redirect(Status::MovedPermanently, uri.try_into().ok())
146    }
147
148    pub fn map_uri<U: TryInto<Reference<'static>>>(
149        self,
150        f: impl FnOnce(Reference<'static>) -> U,
151    ) -> Redirect {
152        Redirect(self.0, self.1.and_then(|p| f(p).try_into().ok()))
153    }
154}
155
156/// Constructs a response with the appropriate status code and the given URL in
157/// the `Location` header field. The body of the response is empty. If the URI
158/// value used to create the `Responder` is an invalid URI, an error of
159/// `Status::InternalServerError` is returned.
160impl<'r> Responder<'r, 'static> for Redirect {
161    fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
162        if let Some(uri) = self.1 {
163            Response::build()
164                .status(self.0)
165                .raw_header("Location", uri.to_string())
166                .ok()
167        } else {
168            error!("Invalid URI used for redirect.");
169            Err(Status::InternalServerError)
170        }
171    }
172}