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