rocket/response/
redirect.rs

1use crate::request::Request;
2use crate::response::{self, Response, Responder};
3use crate::http::uri::Reference;
4use crate::http::Status;
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;
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)]
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;
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;
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;
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;
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;
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
149/// Constructs a response with the appropriate status code and the given URL in
150/// the `Location` header field. The body of the response is empty. If the URI
151/// value used to create the `Responder` is an invalid URI, an error of
152/// `Status::InternalServerError` is returned.
153impl<'r> Responder<'r, 'static> for Redirect {
154    fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
155        if let Some(uri) = self.1 {
156            Response::build()
157                .status(self.0)
158                .raw_header("Location", uri.to_string())
159                .ok()
160        } else {
161            error!("Invalid URI used for redirect.");
162            Err(Status::InternalServerError)
163        }
164    }
165}