Skip to content

Commit 0f9bcb3

Browse files
committed
Refactor the way redirects work
1 parent dd4436f commit 0f9bcb3

File tree

8 files changed

+162
-172
lines changed

8 files changed

+162
-172
lines changed

examples/graphql.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use async_std::task;
22
use juniper::RootNode;
33
use std::sync::RwLock;
4-
use tide::{redirect, Request, Response, Server, StatusCode};
4+
use tide::{Redirect, Request, Response, Server, StatusCode};
55

66
#[derive(Clone)]
77
struct User {
@@ -104,7 +104,7 @@ fn main() -> std::io::Result<()> {
104104
let mut app = Server::with_state(State {
105105
users: RwLock::new(Vec::new()),
106106
});
107-
app.at("/").get(redirect::permanent("/graphiql"));
107+
app.at("/").get(Redirect::permanent("/graphiql"));
108108
app.at("/graphql").post(handle_graphql);
109109
app.at("/graphiql").get(handle_graphiql);
110110
app.listen("0.0.0.0:8080").await?;

examples/redirect.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use tide::{http::StatusCode, Redirect, Response};
2+
3+
#[async_std::main]
4+
async fn main() -> Result<(), std::io::Error> {
5+
let mut app = tide::new();
6+
app.at("/").get(|_| async move { Ok("Root") });
7+
8+
// Redirect hackers to YouTube.
9+
app.at("/.env").get(Redirect::see_other(
10+
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
11+
));
12+
13+
app.at("/users-page").get(|_| async move {
14+
Ok(if signed_in() {
15+
Response::new(StatusCode::Ok)
16+
} else {
17+
// If the user is not signed in then lets redirect them to home page.
18+
Redirect::see_other("/").into()
19+
})
20+
});
21+
22+
app.listen("127.0.0.1:8080").await?;
23+
Ok(())
24+
}
25+
26+
fn signed_in() -> bool {
27+
false
28+
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ pub mod log;
200200
pub mod prelude;
201201
pub mod redirect;
202202
pub mod security;
203+
204+
pub use endpoint::Endpoint;
205+
pub use redirect::Redirect;
206+
pub use request::Request;
203207
pub mod sse;
204208

205209
pub use endpoint::Endpoint;

src/redirect.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//! HTTP redirection endpoint.
2+
//!
3+
//! # Examples
4+
//!
5+
//! ```no_run
6+
//! # use futures::executor::block_on;
7+
//! # fn main() -> Result<(), std::io::Error> { block_on(async {
8+
//! #
9+
//! use tide::Redirect;
10+
//!
11+
//! let mut app = tide::new();
12+
//! app.at("/").get(|_| async move { Ok("meow") });
13+
//! app.at("/nori").get(Redirect::temporary("/"));
14+
//! app.listen("127.0.0.1:8080").await?;
15+
//! #
16+
//! # Ok(()) }) }
17+
//! ```
18+
19+
use crate::utils::BoxFuture;
20+
use crate::StatusCode;
21+
use crate::{Endpoint, Request, Response};
22+
23+
/// A redirection endpoint.
24+
#[derive(Debug, Clone)]
25+
pub struct Redirect<T: AsRef<str>> {
26+
status: StatusCode,
27+
location: T,
28+
}
29+
30+
impl<T: AsRef<str>> Redirect<T> {
31+
/// Creates an endpoint that represents a permanent redirect to `location`.
32+
///
33+
///
34+
/// # Example
35+
///
36+
/// ```
37+
/// # use tide::{Response, Redirect, Request, StatusCode};
38+
/// # fn canonicalize(uri: &url::Url) -> Option<&url::Url> { None }
39+
/// # #[allow(dead_code)]
40+
/// async fn route_handler(request: Request<()>) -> tide::Result {
41+
/// if let Some(canonical_redirect) = canonicalize(request.uri()) {
42+
/// Ok(Redirect::permanent(canonical_redirect).into())
43+
/// } else {
44+
/// //...
45+
/// # Ok(Response::new(StatusCode::Ok)) // ...
46+
/// }
47+
/// }
48+
/// ```
49+
pub fn permanent(location: T) -> Self {
50+
Self {
51+
status: StatusCode::PermanentRedirect,
52+
location,
53+
}
54+
}
55+
56+
/// Creates an endpoint that represents a temporary redirect to `location`.
57+
///
58+
///
59+
/// # Example
60+
///
61+
/// ```
62+
/// # use tide::{Response, Redirect, Request, StatusCode};
63+
/// # fn special_sale_today() -> Option<String> { None }
64+
/// # #[allow(dead_code)]
65+
/// async fn route_handler(request: Request<()>) -> tide::Result {
66+
/// if let Some(sale_url) = special_sale_today() {
67+
/// Ok(Redirect::temporary(sale_url).into())
68+
/// } else {
69+
/// //...
70+
/// # Ok(Response::new(StatusCode::Ok)) //...
71+
/// }
72+
/// }
73+
/// ```
74+
pub fn temporary(location: T) -> Self {
75+
Self {
76+
status: StatusCode::TemporaryRedirect,
77+
location,
78+
}
79+
}
80+
81+
/// Creates an endpoint that represents a see other redirect to `location`.
82+
///
83+
/// GET methods are unchanged.
84+
/// Other methods are changed to GET and their body lost.
85+
///
86+
/// # Example
87+
///
88+
/// ```
89+
/// # use tide::{Response, Redirect, Request, StatusCode};
90+
/// # fn next_product() -> Option<String> { None }
91+
/// # #[allow(dead_code)]
92+
/// async fn route_handler(request: Request<()>) -> tide::Result {
93+
/// if let Some(product_url) = next_product() {
94+
/// Ok(Redirect::see_other(product_url).into())
95+
/// } else {
96+
/// //...
97+
/// # Ok(Response::new(StatusCode::Ok)) //...
98+
/// }
99+
/// }
100+
/// ```
101+
pub fn see_other(location: T) -> Self {
102+
Self {
103+
status: StatusCode::SeeOther,
104+
location,
105+
}
106+
}
107+
108+
/// Returns response with equivalent redirect.
109+
pub fn response(&self) -> Response {
110+
Response::new(self.status).set_header("location".parse().unwrap(), &self.location)
111+
}
112+
}
113+
114+
impl<State, T> Endpoint<State> for Redirect<T>
115+
where
116+
T: AsRef<str> + Send + Sync + 'static,
117+
{
118+
fn call<'a>(&'a self, _req: Request<State>) -> BoxFuture<'a, crate::Result<Response>> {
119+
let res = self.response();
120+
Box::pin(async move { Ok(res) })
121+
}
122+
}
123+
124+
impl<T: AsRef<str>> Into<Response> for Redirect<T> {
125+
fn into(self) -> Response {
126+
self.response()
127+
}
128+
}

src/redirect/mod.rs

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/redirect/permanent.rs

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/redirect/temporary.rs

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/response.rs

Lines changed: 0 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -47,76 +47,6 @@ impl Response {
4747
}
4848
}
4949

50-
/// Creates a response that represents a permanent redirect to `location`.
51-
///
52-
///
53-
/// # Example
54-
///
55-
/// ```
56-
/// # use tide::{Response, Request, StatusCode};
57-
/// # fn canonicalize(uri: &tide::http::Url) -> Option<&tide::http::Url> { None }
58-
/// # #[allow(dead_code)]
59-
/// async fn route_handler(request: Request<()>) -> tide::Result {
60-
/// if let Some(canonical_redirect) = canonicalize(request.uri()) {
61-
/// Ok(Response::redirect_permanent(canonical_redirect))
62-
/// } else {
63-
/// //...
64-
/// # Ok(Response::new(StatusCode::Ok)) // ...
65-
/// }
66-
/// }
67-
/// ```
68-
pub fn redirect_permanent(location: impl AsRef<str>) -> Self {
69-
Response::new(StatusCode::PermanentRedirect)
70-
.set_header("location".parse().unwrap(), location)
71-
}
72-
73-
/// Creates a response that represents a temporary redirect to `location`.
74-
///
75-
///
76-
/// # Example
77-
///
78-
/// ```
79-
/// # use tide::{Response, Request, StatusCode};
80-
/// # fn special_sale_today() -> Option<String> { None }
81-
/// # #[allow(dead_code)]
82-
/// async fn route_handler(request: Request<()>) -> tide::Result {
83-
/// if let Some(sale_url) = special_sale_today() {
84-
/// Ok(Response::redirect_temporary(sale_url))
85-
/// } else {
86-
/// //...
87-
/// # Ok(Response::new(StatusCode::Ok)) //...
88-
/// }
89-
/// }
90-
/// ```
91-
pub fn redirect_temporary(location: impl AsRef<str>) -> Self {
92-
Response::new(StatusCode::TemporaryRedirect)
93-
.set_header("location".parse().unwrap(), location)
94-
}
95-
96-
/// Creates a response that represents a see other redirect to `location`.
97-
///
98-
/// GET methods are unchanged.
99-
/// Other methods are changed to GET and their body lost.
100-
///
101-
/// # Example
102-
///
103-
/// ```
104-
/// # use tide::{Response, Request, StatusCode};
105-
/// # fn next_product() -> Option<String> { None }
106-
/// # #[allow(dead_code)]
107-
/// async fn route_handler(request: Request<()>) -> tide::Result {
108-
/// if let Some(product_url) = next_product() {
109-
/// Ok(Response::redirect_see_other(product_url))
110-
/// } else {
111-
/// //...
112-
/// # Ok(Response::new(StatusCode::Ok)) //...
113-
/// }
114-
/// }
115-
/// ```
116-
pub fn redirect_see_other(location: impl AsRef<str>) -> Self {
117-
Response::new(StatusCode::SeeOther).set_header("location".parse().unwrap(), location)
118-
}
119-
12050
/// Returns the statuscode.
12151
pub fn status(&self) -> crate::StatusCode {
12252
self.res.status()

0 commit comments

Comments
 (0)