路由基础[20260807214219]

0 阅读1分钟

路由基础

项目代码:github.com/hyperlane-d…

简介

路由是 HTTP 服务器将传入请求映射到相应处理器的机制。在 Hyperlane 中,路由既灵活又强大,支持静态路由、动态路径参数、基于正则的匹配和路由过滤。本文将介绍在 Hyperlane 中设置路由所需了解的一切。

静态路由

最简单的路由形式是静态路由,它匹配精确的 URL 路径:

server.route::<Route>("/test");

这注册了一个仅匹配精确路径 /test 的路由。任何其他路径都不会由此路由处理。静态路由非常适合固定端点,如 /health/about/api/status

动态路由

动态路由包含路径参数,用于捕获 URL 的可变部分。参数用花括号 {...} 括起来:

server.route::<Route>("/test/{text}");

在这个示例中,{text} 是一个动态参数,匹配任何单一路径段。例如:

  • /test/hellotext = "hello"
  • /test/worldtext = "world"
  • /test/123text = "123"

但是,/test/hello/world 不会匹配,因为 {text} 只匹配单个段。

正则动态路由

为了更精确地控制参数匹配的内容,可以使用正则表达式模式:

server.route::<Route>("/test/{number:\\d+}");

在这个示例中,{number:\\d+} 仅匹配一个或多个数字。例如:

  • /test/123number = "123"(匹配)
  • /test/abc — 不匹配(包含非数字字符)
  • /test/42number = "42"(匹配)

正则语法遵循 Rust 的 regex crate,为你的路由提供了强大的模式匹配能力。

路由参数

路由匹配后,可以从上下文中提取参数值:

获取单个参数

// 返回 Option<String> — 如果参数不存在则返回 None
let param: Option<String> = ctx.try_get_route_param("text");

// 直接返回 String — 如果参数不存在会 panic
let param: String = ctx.get_route_param("text");

当你不确定参数是否存在时,使用 try_get_route_param;当参数肯定存在时(因为路由模式要求它),使用 get_route_param

获取所有参数

let params = ctx.get_route_params();

这将返回所有路由参数的集合,当你路由中有多个动态段时很有用。

路由过滤

Hyperlane 提供了一个强大的路由过滤系统,允许你为路由添加条件。过滤器作为路由结构体上的属性宏实现:

基于 Host 过滤

根据 Host 头部匹配请求:

#[host("example.com")]

此路由仅匹配 Host 头部为 example.com 的请求。

拒绝 Host 过滤

阻止来自特定主机的请求:

#[reject_host("blocked.com")]

此路由拒绝 Host 头部为 blocked.com 的请求。

基于 Referer 过滤

根据 Referer 头部匹配请求:

#[referer("https://example.com")]

此路由仅匹配来自 https://example.com 的请求。

拒绝 Referer 过滤

阻止来自特定来源的请求:

#[reject_referer("https://malicious.com")]

此路由拒绝来自 https://malicious.com 的请求。

方法过滤

按 HTTP 方法过滤请求:

#[filter(ctx.get_request().get_method() == &RequestMethod::Get)]

此路由仅匹配 GET 请求。你可以使用任何计算结果为布尔值的表达式。

拒绝过滤

拒绝匹配特定条件的请求:

#[reject(ctx.get_request().get_path().len() > 1000)]

此路由拒绝路径长度超过 1000 个字符的请求。

属性宏路由

可以以编程方式注册路由,也可以使用 #[route] 属性宏:

#[route("/test/{text}")]
struct Route;

这种声明式方法更简洁,并将路由路径保持在处理器实现附近。宏会自动处理注册。

组合过滤器

可以在单个路由上组合多个过滤器,实现精细控制:

#[host("example.com")]
#[reject_host("blocked.com")]
#[referer("https://example.com")]
#[reject_referer("https://malicious.com")]
#[filter(ctx.get_request().get_method() == &RequestMethod::Get)]
#[reject(ctx.get_request().get_path().len() > 1000)]

此路由仅在满足以下所有条件时才匹配:

  1. 主机是 example.com
  2. 主机不是 blocked.com
  3. 来源是 https://example.com
  4. 来源不是 https://malicious.com
  5. 请求方法是 GET
  6. 路径长度不超过 1000

完整路由示例

以下是一个展示各种路由模式的完整示例:

use hyperlane::*;
use hyperlane_macros::*;

// 静态路由
#[route("/health")]
struct HealthRoute;

// 带文本参数的动态路由
#[route("/users/{name}")]
struct UserRoute;

// 带数字参数的正则动态路由
#[route("/posts/{id:\\d+}")]
struct PostRoute;

// 带主机过滤的路由
#[route("/admin")]
#[host("admin.example.com")]
struct AdminRoute;

// 带方法过滤的路由
#[route("/api/data")]
#[filter(ctx.get_request().get_method() == &RequestMethod::Get)]
struct ApiDataRoute;

#[tokio::main]
async fn main() {
    let mut server: Server = Server::default();
    let server_control_hook: ServerControlHook = server.run().await.unwrap_or_default();
    server_control_hook.wait().await;
}

路由匹配顺序

当多个路由可能匹配一个请求时,Hyperlane 使用以下优先级:

  1. 静态路由 优先于动态路由
  2. 更具体的路由 优先于不太具体的路由
  3. 带过滤器的路由 在路径匹配后进行评估

这意味着如果你同时有 /users/{name}/users/admin,对 /users/admin 的请求会先匹配静态路由。

最佳实践

  1. 尽可能使用静态路由:它们比动态路由更快、更可预测。

  2. 对类型化参数使用正则约束:如果参数应该是数字,使用 {id:\\d+} 而不是 {id}

  3. 保持路由路径简洁:深层嵌套的路径如 /api/v1/users/{id}/posts/{post_id}/comments/{comment_id} 可能难以维护。考虑扁平化 URL 结构。

  4. 使用过滤器进行访问控制:主机和方法过滤器比在处理器中检查这些条件更高效。

  5. 使用属性宏语法:它比编程式路由注册更具可读性和可维护性。

总结

Hyperlane 的路由系统提供了一套全面的工具,用于将 URL 映射到处理器。从简单的静态路由到具有多个过滤器的复杂正则模式,该系统旨在处理任何路由需求。属性宏语法使路由定义简洁且声明式,而编程式 API 在你需要时提供完全的控制。

在下一篇文章中,我们将探讨如何处理传入的请求,包括提取头部、查询参数和请求体数据。


项目代码:github.com/hyperlane-d…