Rust in Everything
基于 axum 0.8.9

路由

上一部分讲到 handler 返回 Response 为止。这一部分往外走一层:Router 是什么、一个请求怎么在它里面找到 handler,以及为了让各种 handler 和服务住进同一张表,axum 付出了什么。路由分两级:先按路径(matchit 树),再按方法(MethodRouter)。两级之下的端点都是同一个类型 Route。

  1. Router 的内部结构:Router 是 Arc<RouterInner>,里面两个 PathRouter(路由和 fallback)加一个兜底的 catch_all_fallback;matchit 树只存 RouteId,端点在旁边的表里;改动时 Arc::try_unwrap 成功就原地改,否则复制全部端点;请求依次交给三处,匹配上的写 MatchedPath 和路径参数。
  2. matchit 与路径语法:matchit 0.8.4 的 /{id}、/{*rest},0.7 的 /:id 写法在 0.8 会 panic 并提示 without_v07_checks;哪些路由算冲突、在注册时就 panic。
  3. MethodRouter:每个 HTTP 方法一个槽,HEAD 请求在没有 head 槽时交给 get 槽;没有匹配的方法时回 405 并带 Allow 头;同一方法注册两次 panic。
  4. nest、merge 与 fallback:nest 把子路由加上前缀摊平进父路由树,nest_service 把子服务整个挂上去;merge 合并两棵树;fallback 在嵌套与合并时归谁。
  5. Route 与类型擦除:Route 是 BoxCloneSyncService 的包装,每个请求克隆一次、future 装箱一次,每多一层 layer 再多一份;欠状态的 handler 先存成 BoxedIntoRoute;最外层的 RouteFuture 补 Content-Length、清空 HEAD 的 body。
flowchart TB
  req["Request"] --> router["Router:Arc RouterInner"]
  router --> pathr["PathRouter:按路径"]
  pathr --> ep{"Endpoint"}
  ep -->|"route"| mr["MethodRouter:按方法"]
  ep -->|"route_service、nest_service"| rt["Route"]
  mr --> rt2["方法槽里的 Route"]
  rt --> svc["handler 或服务"]
  rt2 --> svc

第 1 章和第 5 章讲两头的数据结构,中间三章讲规则。中间件怎么套到这些 Route 上,在下一部分的 layer 与 route_layer。

💬 关联讨论

发起讨论
加载中...