Rust in Everything
基于 axum 0.8.9

Handler 与提取器

这是本卷的重点。axum 没有路由宏,处理函数就是 async fn,它的每个参数都是一个「提取器」:一个能从请求里构造出自己的类型。函数能被 get(...) 接受,靠的是 Handler trait 对 1–16 个参数的函数的宏实现;每个参数怎么从请求里来,靠的是 axum-core 的两个提取器 trait。常见坑里一半的编译错误都出在这一部分。

前两章讲机制,后三章讲具体的提取器:

  1. Handler trait:Handler<T, S> 的 T 是参数类型的元组,用来绕开一致性规则;impl_handler! 经 all_the_tuples! 展开出 1–16 个参数的 impl,call 从左到右跑提取器,任何一个失败就返回 rejection;HandlerService 把 handler 和状态包成 tower Service。
  2. FromRequestParts 与 FromRequest:前者借 &mut Parts、可以有多个,后者拿走整个请求、只能放最后,所以 body 只会被读一次;ViaParts 标记让只看头的提取器也能放最后;0.8 起 Option<T> 要求 OptionalFromRequestParts,不再吞掉所有错误。
  3. State 与 FromRef:Router<S> 的 S 是还欠着的状态类型,with_state 之后变成 Router<()>;FromRef 从总状态里取出子状态;State 在编译期检查,Extension 在运行时查找。
  4. Path、Query 与请求元数据:Path 用自己的 serde Deserializer 读 matchit 抓到的参数,Query 用 serde_urlencoded;错误怎么分类、回什么状态码;MatchedPath、OriginalUri、ConnectInfo 这些元数据从哪来。
  5. body 提取器:Json、Form、Bytes、String、Multipart 怎么读 body;DefaultBodyLimit 的 2 MB 默认上限在哪里生效;各种 rejection 对应的 400 / 413 / 415 / 422。
flowchart TB
  fnitem["async fn 处理函数"] --> handler["Handler trait"]
  handler --> parts["FromRequestParts:前面的参数"]
  handler --> whole["FromRequest:最后一个参数"]
  parts --> st["State"]
  parts --> pq["Path、Query、元数据"]
  whole --> bodyx["Json、Form、Bytes 等"]
  whole -.->|"ViaParts"| parts

Handler 决定参数按什么顺序、以什么身份被提取;两个提取器 trait 决定每个参数能看到请求的哪一部分;后三章是这两个 trait 的具体实现。返回值那一侧的 IntoResponse 在下一部分的 IntoResponse 与 IntoResponseParts。

💬 关联讨论

发起讨论
加载中...