1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-23 16:21:06 +01:00

add method to extract matched resource name (#1577)

Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
Takashi Idobe 2020-06-27 11:22:16 -04:00 committed by GitHub
parent 487f90be5b
commit 23c8191cca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 99 additions and 1 deletions

View File

@ -7,6 +7,7 @@
* Re-export `actix_rt::main` as `actix_web::main`.
* `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched
resource pattern.
* `HttpRequest::match_name` and `ServiceRequest::match_name` for extracting matched resource name.
### Changed

View File

@ -137,6 +137,14 @@ impl HttpRequest {
self.0.rmap.match_pattern(self.path())
}
/// The resource name that matched the path. Useful for logging and metrics.
///
/// Returns a None when no resource is fully matched, including default services.
#[inline]
pub fn match_name(&self) -> Option<&str> {
self.0.rmap.match_name(self.path())
}
/// Request extensions
#[inline]
pub fn extensions(&self) -> Ref<'_, Extensions> {
@ -462,6 +470,24 @@ mod tests {
);
}
#[test]
fn test_match_name() {
let mut rdef = ResourceDef::new("/index.html");
*rdef.name_mut() = "index".to_string();
let mut rmap = ResourceMap::new(ResourceDef::new(""));
rmap.add(&mut rdef, None);
assert!(rmap.has_resource("/index.html"));
let req = TestRequest::default()
.uri("/index.html")
.rmap(rmap)
.to_http_request();
assert_eq!(req.match_name(), Some("index"));
}
#[test]
fn test_url_for_external() {
let mut rdef = ResourceDef::new("https://youtube.com/watch/{video_id}");

View File

@ -93,6 +93,27 @@ impl ResourceMap {
false
}
/// Returns the name of the route that matches the given path or None if no full match
/// is possible.
pub fn match_name(&self, path: &str) -> Option<&str> {
let path = if path.is_empty() { "/" } else { path };
for (pattern, rmap) in &self.patterns {
if let Some(ref rmap) = rmap {
if let Some(plen) = pattern.is_prefix_match(path) {
return rmap.match_name(&path[plen..]);
}
} else if pattern.is_match(path) {
return match pattern.name() {
"" => None,
s => Some(s),
};
}
}
None
}
/// Returns the full resource pattern matched against a path or None if no full match
/// is possible.
pub fn match_pattern(&self, path: &str) -> Option<String> {
@ -302,4 +323,48 @@ mod tests {
Some("/user/{id}/post/{post_id}/comment/{comment_id}".to_owned())
);
}
#[test]
fn extract_matched_name() {
let mut root = ResourceMap::new(ResourceDef::root_prefix(""));
let mut rdef = ResourceDef::new("/info");
*rdef.name_mut() = "root_info".to_owned();
root.add(&mut rdef, None);
let mut user_map = ResourceMap::new(ResourceDef::root_prefix(""));
let mut rdef = ResourceDef::new("/");
user_map.add(&mut rdef, None);
let mut rdef = ResourceDef::new("/post/{post_id}");
*rdef.name_mut() = "user_post".to_owned();
user_map.add(&mut rdef, None);
root.add(
&mut ResourceDef::root_prefix("/user/{id}"),
Some(Rc::new(user_map)),
);
let root = Rc::new(root);
root.finish(Rc::clone(&root));
// sanity check resource map setup
assert!(root.has_resource("/info"));
assert!(!root.has_resource("/bar"));
assert!(root.has_resource("/user/22"));
assert!(root.has_resource("/user/22/"));
assert!(root.has_resource("/user/22/post/55"));
// extract patterns from paths
assert!(root.match_name("/bar").is_none());
assert!(root.match_name("/v44").is_none());
assert_eq!(root.match_name("/info"), Some("root_info"));
assert_eq!(root.match_name("/user/22"), None);
assert_eq!(root.match_name("/user/22/"), None);
assert_eq!(root.match_name("/user/22/post/55"), Some("user_post"));
}
}

View File

@ -195,7 +195,13 @@ impl ServiceRequest {
pub fn match_info(&self) -> &Path<Url> {
self.0.match_info()
}
/// Counterpart to [`HttpRequest::match_name`](../struct.HttpRequest.html#method.match_name).
#[inline]
pub fn match_name(&self) -> Option<&str> {
self.0.match_name()
}
/// Counterpart to [`HttpRequest::match_pattern`](../struct.HttpRequest.html#method.match_pattern).
#[inline]
pub fn match_pattern(&self) -> Option<String> {