Skip to content

Commit 6ed507b

Browse files
committed
Expose the matched route as a method on the request struct
If the router found a matching route we store it as an extension struct on the request and then expose it to the user as a method on `Request`. Signed-off-by: Johannes Löthberg <[email protected]>
1 parent e79e4f6 commit 6ed507b

File tree

4 files changed

+58
-5
lines changed

4 files changed

+58
-5
lines changed

src/request.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use async_std::io::{self, prelude::*};
22
use async_std::task::{Context, Poll};
33
use routefinder::Captures;
44

5-
use std::ops::Index;
5+
use std::ops::{Deref, Index};
66
use std::pin::Pin;
77

88
#[cfg(feature = "cookies")]
@@ -12,6 +12,7 @@ use crate::http::cookies::Cookie;
1212
use crate::http::format_err;
1313
use crate::http::headers::{self, HeaderName, HeaderValues, ToHeaderValues};
1414
use crate::http::{self, Body, Method, Mime, StatusCode, Url, Version};
15+
use crate::route::MatchedRoute;
1516
use crate::Response;
1617

1718
pin_project_lite::pin_project! {
@@ -266,6 +267,24 @@ impl<State> Request<State> {
266267
&self.state
267268
}
268269

270+
/// Get the matched route.
271+
///
272+
/// Returns the route as a `&str`, borrowed from this `Request`.
273+
///
274+
/// # Examples
275+
///
276+
/// ```
277+
/// # use tide::Request;
278+
/// let mut app = tide::Server::new();
279+
/// app.at("/route").get(|req: Request<()>| async move {
280+
/// let route = req.route().unwrap_or("No route found");
281+
/// Ok(route.to_string())
282+
/// });
283+
/// ```
284+
pub fn route(&self) -> Option<&str> {
285+
self.ext::<MatchedRoute>().map(|r| r.deref())
286+
}
287+
269288
/// Extract and parse a route parameter by name.
270289
///
271290
/// Returns the parameter as a `&str`, borrowed from this `Request`.

src/route.rs

+12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ use crate::{router::Router, Endpoint, Middleware};
99

1010
use kv_log_macro::trace;
1111

12+
/// Extension struct for storing the matched route in the request.
13+
#[derive(Debug)]
14+
pub(crate) struct MatchedRoute(pub(crate) String);
15+
16+
impl std::ops::Deref for MatchedRoute {
17+
type Target = str;
18+
19+
fn deref(&self) -> &Self::Target {
20+
&self.0
21+
}
22+
}
23+
1224
/// A handle to a route.
1325
///
1426
/// All HTTP requests are made against resources. After using [`Server::at`] (or

src/router.rs

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ impl<State> std::fmt::Debug for Router<State> {
2727
pub(crate) struct Selection<'a, State> {
2828
pub(crate) endpoint: &'a DynEndpoint<State>,
2929
pub(crate) params: Captures<'static, 'static>,
30+
pub(crate) matched_route: Option<String>,
3031
}
3132

3233
impl<State: Clone + Send + Sync + 'static> Router<State> {
@@ -63,11 +64,13 @@ impl<State: Clone + Send + Sync + 'static> Router<State> {
6364
Selection {
6465
endpoint: m.handler(),
6566
params: m.captures().into_owned(),
67+
matched_route: Some(m.route().to_string()),
6668
}
6769
} else if let Some(m) = self.all_method_router.best_match(path) {
6870
Selection {
6971
endpoint: m.handler(),
7072
params: m.captures().into_owned(),
73+
matched_route: Some(m.route().to_string()),
7174
}
7275
} else if method == http_types::Method::Head {
7376
// If it is a HTTP HEAD request then check if there is a callback in the endpoints map
@@ -85,11 +88,13 @@ impl<State: Clone + Send + Sync + 'static> Router<State> {
8588
Selection {
8689
endpoint: &method_not_allowed,
8790
params: Captures::default(),
91+
matched_route: None,
8892
}
8993
} else {
9094
Selection {
9195
endpoint: &not_found_endpoint,
9296
params: Captures::default(),
97+
matched_route: None,
9398
}
9499
}
95100
}

src/server.rs

+21-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use kv_log_macro::{info, trace};
88
use crate::cookies;
99
use crate::listener::{Listener, ToListener};
1010
use crate::middleware::{Middleware, Next};
11+
use crate::route::MatchedRoute;
1112
use crate::router::{Router, Selection};
1213
use crate::{Endpoint, Request, Route};
1314

@@ -287,9 +288,17 @@ where
287288
} = self.clone();
288289

289290
let method = req.method().to_owned();
290-
let Selection { endpoint, params } = router.route(req.url().path(), method);
291+
let Selection {
292+
endpoint,
293+
params,
294+
matched_route,
295+
} = router.route(req.url().path(), method);
291296
let route_params = vec![params];
292-
let req = Request::new(state, req, route_params);
297+
let mut req = Request::new(state, req, route_params);
298+
299+
if let Some(route) = matched_route {
300+
req.set_ext(MatchedRoute(route));
301+
}
293302

294303
let next = Next {
295304
endpoint,
@@ -349,9 +358,17 @@ impl<State: Clone + Sync + Send + 'static, InnerState: Clone + Sync + Send + 'st
349358
let middleware = self.middleware.clone();
350359
let state = self.state.clone();
351360

352-
let Selection { endpoint, params } = router.route(&path, method);
361+
let Selection {
362+
endpoint,
363+
params,
364+
matched_route,
365+
} = router.route(&path, method);
353366
route_params.push(params);
354-
let req = Request::new(state, req, route_params);
367+
let mut req = Request::new(state, req, route_params);
368+
369+
if let Some(route) = matched_route {
370+
req.set_ext(MatchedRoute(route));
371+
}
355372

356373
let next = Next {
357374
endpoint,

0 commit comments

Comments
 (0)