matchit
一个高性能、零拷贝的URL路由器。
use matchit::Router;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut router = Router::new();
router.insert("/home", "欢迎!")?;
router.insert("/users/{id}", "一个用户")?;
let matched = router.at("/users/978")?;
assert_eq!(matched.params.get("id"), Some("978"));
assert_eq!(*matched.value, "一个用户");
Ok(())
}
参数
路由器支持动态路由段。这些可以是命名参数或通配符参数。
像/{id}
这样的命名参数可以匹配任何内容,直到下一个/
或路径末尾。请注意,命名参数后面必须跟着/
或路由的末尾。目前不支持动态后缀。
let mut m = Router::new();
m.insert("/users/{id}", true)?;
assert_eq!(m.at("/users/1")?.params.get("id"), Some("1"));
assert_eq!(m.at("/users/23")?.params.get("id"), Some("23"));
assert!(m.at("/users").is_err());
通配符参数以*
开头,匹配路径末尾的任何内容。它们必须始终位于路由的末尾。
let mut m = Router::new();
m.insert("/{*p}", true)?;
assert_eq!(m.at("/foo.js")?.params.get("p"), Some("foo.js"));
assert_eq!(m.at("/c/bar.css")?.params.get("p"), Some("c/bar.css"));
// 注意,这不会匹配
assert!(m.at("/").is_err());
字面字符{
和}
可以通过使用相同的字符进行转义来包含在静态路由中。例如,{
字符用{{
转义,}
字符用}}
转义。
let mut m = Router::new();
m.insert("/{{hello}}", true)?;
m.insert("/{hello}", true)?;
// 匹配静态路由
assert!(m.at("/{hello}")?.value);
// 匹配动态路由
assert_eq!(m.at("/hello")?.params.get("hello"), Some("hello"));
路由优先级
允许静态和动态路由段重叠。如果它们重叠,静态段将被赋予更高的优先级:
let mut m = Router::new();
m.insert("/", "欢迎!").unwrap(); // 优先级:1
m.insert("/about", "关于我").unwrap(); // 优先级:1
m.insert("/{*filepath}", "...").unwrap(); // 优先级:2
它是如何工作的?
路由器利用了URL路由通常遵循层次结构的事实。路由被存储在一个大量使用共同前缀的基数树中。
优先级 路径 值
9 \ 1
3 ├s 无
2 |├earch\ 2
1 |└upport\ 3
2 ├blog\ 4
1 | └{post} 无
1 | └\ 5
2 ├about-us\ 6
1 | └team\ 7
1 └contact\ 8
这使我们可以将路由搜索减少到少量的分支。同一层级的子节点也按注册值的子节点数量进行优先级排序,增加了第一次尝试就选择正确分支的机会。
基准测试
事实证明,这种路由方法非常快。在一个将4个路径与130个注册路由进行匹配的基准测试中,matchit
能在200纳秒内找到正确的路由,比大多数其他路由器快一个数量级。你可以在这里查看基准测试代码。
比较路由器/matchit
时间: [175.96 ns 176.39 ns 176.84 ns]
比较路由器/actix
时间: [26.805 us 26.811 us 26.816 us]
比较路由器/path-tree
时间: [468.95 ns 470.34 ns 471.65 ns]
比较路由器/regex
时间: [22.539 us 22.584 us 22.639 us]
比较路由器/route-recognizer
时间: [3.7552 us 3.7732 us 3.8027 us]
比较路由器/routefinder
时间: [5.7313 us 5.7405 us 5.7514 us]
致谢
这个包中的大量代码基于Julien Schmidt的httprouter
。