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}