Skip to content

Commit f169f9c

Browse files
authored
Merge pull request #513 from ethanboxx/better-redirects
Better redirects
2 parents 0bd964a + e693a78 commit f169f9c

File tree

8 files changed

+145
-131
lines changed

8 files changed

+145
-131
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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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")
10+
.get(Redirect::new("https://www.youtube.com/watch?v=dQw4w9WgXcQ"));
11+
12+
app.at("/users-page").get(|_| async move {
13+
Ok(if signed_in() {
14+
Response::new(StatusCode::Ok)
15+
} else {
16+
// If the user is not signed in then lets redirect them to home page.
17+
Redirect::new("/").into()
18+
})
19+
});
20+
21+
app.listen("127.0.0.1:8080").await?;
22+
Ok(())
23+
}
24+
25+
fn signed_in() -> bool {
26+
false
27+
}

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ mod cookies;
189189
mod endpoint;
190190
mod fs;
191191
mod middleware;
192+
mod redirect;
192193
mod request;
193194
mod response;
194195
mod route;
@@ -198,12 +199,12 @@ mod utils;
198199

199200
pub mod log;
200201
pub mod prelude;
201-
pub mod redirect;
202202
pub mod security;
203203
pub mod sse;
204204

205205
pub use endpoint::Endpoint;
206206
pub use middleware::{Middleware, Next};
207+
pub use redirect::Redirect;
207208
pub use request::Request;
208209
pub use response::Response;
209210
pub use route::Route;

src/redirect.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//! HTTP redirection endpoint.
2+
//!
3+
//! # Examples
4+
//!
5+
//! ```no_run
6+
//! # use async_std::task::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+
///
25+
/// # Example
26+
///
27+
/// ```
28+
/// # use tide::{Response, Redirect, Request, StatusCode};
29+
/// # fn next_product() -> Option<String> { None }
30+
/// # #[allow(dead_code)]
31+
/// async fn route_handler(request: Request<()>) -> tide::Result {
32+
/// if let Some(product_url) = next_product() {
33+
/// Ok(Redirect::new(product_url).into())
34+
/// } else {
35+
/// //...
36+
/// # Ok(Response::new(StatusCode::Ok)) //...
37+
/// }
38+
/// }
39+
/// ```
40+
#[derive(Debug, Clone)]
41+
pub struct Redirect<T: AsRef<str>> {
42+
status: StatusCode,
43+
location: T,
44+
}
45+
46+
impl<T: AsRef<str>> Redirect<T> {
47+
/// Creates an endpoint that represents a redirect to `location`.
48+
///
49+
/// Uses status code 302 Found.
50+
pub fn new(location: T) -> Self {
51+
Self {
52+
status: StatusCode::Found,
53+
location,
54+
}
55+
}
56+
57+
/// Creates an endpoint that represents a permanent redirect to `location`.
58+
///
59+
/// Uses status code 301 Permanent Redirect.
60+
pub fn permanent(location: T) -> Self {
61+
Self {
62+
status: StatusCode::PermanentRedirect,
63+
location,
64+
}
65+
}
66+
67+
/// Creates an endpoint that represents a temporary redirect to `location`.
68+
///
69+
/// Uses status code 307 Temporary Redirect.
70+
pub fn temporary(location: T) -> Self {
71+
Self {
72+
status: StatusCode::TemporaryRedirect,
73+
location,
74+
}
75+
}
76+
77+
/// Creates an endpoint that represents a see other redirect to `location`.
78+
///
79+
/// Uses status code 303 See Other.
80+
pub fn see_other(location: T) -> Self {
81+
Self {
82+
status: StatusCode::SeeOther,
83+
location,
84+
}
85+
}
86+
}
87+
88+
impl<State, T> Endpoint<State> for Redirect<T>
89+
where
90+
T: AsRef<str> + Send + Sync + 'static,
91+
{
92+
fn call<'a>(&'a self, _req: Request<State>) -> BoxFuture<'a, crate::Result<Response>> {
93+
let res = self.into();
94+
Box::pin(async move { Ok(res) })
95+
}
96+
}
97+
98+
impl<T: AsRef<str>> Into<Response> for Redirect<T> {
99+
fn into(self) -> Response {
100+
(&self).into()
101+
}
102+
}
103+
104+
impl<T: AsRef<str>> Into<Response> for &Redirect<T> {
105+
fn into(self) -> Response {
106+
Response::new(self.status).set_header("location".parse().unwrap(), &self.location)
107+
}
108+
}

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: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use serde::Serialize;
77
use crate::http::cookies::Cookie;
88
use crate::http::headers::{HeaderName, HeaderValue};
99
use crate::http::{self, Body, StatusCode};
10+
use crate::Redirect;
1011

1112
#[derive(Debug)]
1213
pub(crate) enum CookieEvent {
@@ -47,31 +48,9 @@ impl Response {
4748
}
4849
}
4950

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`.
51+
/// Creates a response that represents a redirect to `location`.
7452
///
53+
/// Uses status code 302 Found.
7554
///
7655
/// # Example
7756
///
@@ -81,16 +60,15 @@ impl Response {
8160
/// # #[allow(dead_code)]
8261
/// async fn route_handler(request: Request<()>) -> tide::Result {
8362
/// if let Some(sale_url) = special_sale_today() {
84-
/// Ok(Response::redirect_temporary(sale_url))
63+
/// Ok(Response::redirect(sale_url))
8564
/// } else {
8665
/// //...
8766
/// # Ok(Response::new(StatusCode::Ok)) //...
8867
/// }
8968
/// }
9069
/// ```
91-
pub fn redirect_temporary(location: impl AsRef<str>) -> Self {
92-
Response::new(StatusCode::TemporaryRedirect)
93-
.set_header("location".parse().unwrap(), location)
70+
pub fn redirect(location: impl AsRef<str>) -> Self {
71+
Redirect::new(location).into()
9472
}
9573

9674
/// Returns the statuscode.

0 commit comments

Comments
 (0)