1 use axum_core::response::{IntoResponse, Response};
2 use http::{header::LOCATION, HeaderValue, StatusCode};
3 
4 /// Response that redirects the request to another location.
5 ///
6 /// # Example
7 ///
8 /// ```rust
9 /// use axum::{
10 ///     routing::get,
11 ///     response::Redirect,
12 ///     Router,
13 /// };
14 ///
15 /// let app = Router::new()
16 ///     .route("/old", get(|| async { Redirect::permanent("/new") }))
17 ///     .route("/new", get(|| async { "Hello!" }));
18 /// # async {
19 /// # hyper::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
20 /// # };
21 /// ```
22 #[must_use = "needs to be returned from a handler or otherwise turned into a Response to be useful"]
23 #[derive(Debug, Clone)]
24 pub struct Redirect {
25     status_code: StatusCode,
26     location: HeaderValue,
27 }
28 
29 impl Redirect {
30     /// Create a new [`Redirect`] that uses a [`303 See Other`][mdn] status code.
31     ///
32     /// This redirect instructs the client to change the method to GET for the subsequent request
33     /// to the given `uri`, which is useful after successful form submission, file upload or when
34     /// you generally don't want the redirected-to page to observe the original request method and
35     /// body (if non-empty). If you want to preserve the request method and body,
36     /// [`Redirect::temporary`] should be used instead.
37     ///
38     /// # Panics
39     ///
40     /// If `uri` isn't a valid [`HeaderValue`].
41     ///
42     /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303
to(uri: &str) -> Self43     pub fn to(uri: &str) -> Self {
44         Self::with_status_code(StatusCode::SEE_OTHER, uri)
45     }
46 
47     /// Create a new [`Redirect`] that uses a [`307 Temporary Redirect`][mdn] status code.
48     ///
49     /// This has the same behavior as [`Redirect::to`], except it will preserve the original HTTP
50     /// method and body.
51     ///
52     /// # Panics
53     ///
54     /// If `uri` isn't a valid [`HeaderValue`].
55     ///
56     /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307
temporary(uri: &str) -> Self57     pub fn temporary(uri: &str) -> Self {
58         Self::with_status_code(StatusCode::TEMPORARY_REDIRECT, uri)
59     }
60 
61     /// Create a new [`Redirect`] that uses a [`308 Permanent Redirect`][mdn] status code.
62     ///
63     /// # Panics
64     ///
65     /// If `uri` isn't a valid [`HeaderValue`].
66     ///
67     /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308
permanent(uri: &str) -> Self68     pub fn permanent(uri: &str) -> Self {
69         Self::with_status_code(StatusCode::PERMANENT_REDIRECT, uri)
70     }
71 
72     // This is intentionally not public since other kinds of redirects might not
73     // use the `Location` header, namely `304 Not Modified`.
74     //
75     // We're open to adding more constructors upon request, if they make sense :)
with_status_code(status_code: StatusCode, uri: &str) -> Self76     fn with_status_code(status_code: StatusCode, uri: &str) -> Self {
77         assert!(
78             status_code.is_redirection(),
79             "not a redirection status code"
80         );
81 
82         Self {
83             status_code,
84             location: HeaderValue::try_from(uri).expect("URI isn't a valid header value"),
85         }
86     }
87 }
88 
89 impl IntoResponse for Redirect {
into_response(self) -> Response90     fn into_response(self) -> Response {
91         (self.status_code, [(LOCATION, self.location)]).into_response()
92     }
93 }
94