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