知识点思维导图
28 个知识节点
参考资料
Python(14) - FastAPI 入门
读完后,你应能完成以下任务:
- 绘制“Python(14) - FastAPI 入门 / 边界一:装饰器即路由(@app.get 到底是什么)”的关键对象与数据流,解释“Express 里注册路由是「调函数」:app.get(path, fn),把 fn 当参数传进去。”,并用源码位置、日志或 Trace 标注证据。
- 为“Python(14) - FastAPI 入门 / 边界二:没有 req/res——参数靠函数签名,响应靠 return”设计正常与异常输入,验证“这是从 Express 转过来最大的认知转变,务必吃透。”,输出首个偏差位置与回归测试结果。
- 实现“Python(14) - FastAPI 入门 / 白送的接口文档:/docs(Express 要装 Swagger 才有)”的最小代码或配置,检验“另有 http://127.0.0.1:8000/redoc 是另一种风格的只读文档。”,输出命令、结果与 Diff,并说明不适用边界。
你在 Node 端写过
app.get('/users', handler),用 Express 撸过几十个接口。FastAPI 的路由长得几乎一样——但它把「参数校验」「类型转换」「接口文档」这些你在 Express 里要手写或装一堆中间件才能搞定的事,靠 Python 的类型注解直接内置了。本篇帮你把 Express 的心智模型平移过来,并立刻划清三处关键差异:装饰器即路由、函数签名即参数、返回 dict 即响应。
一、先给锚点:FastAPI 路由 ≈ Express 路由
最小可跑的一个接口,左边 Express、右边 FastAPI 并排看:
// Express (Node)
const express = require('express')
const app = express()
// 注册一个 GET 路由,第一个参数是路径,第二个是处理函数
app.get('/', (req, res) => {
res.json({ message: 'hello' }) // 手动调 res.json 序列化
})
app.listen(3000) // 监听 3000 端口
启动它(FastAPI 自己不带服务器,靠 uvicorn 这个 ASGI 服务器跑,详见第 13 篇):
# 安装:fastapi 是框架,uvicorn 是跑它的服务器(类比 node 直接内置了 http server,Python 要单独装)
pip install "fastapi[standard]" uvicorn
# 启动:main 是文件名(main.py),app 是上面那个实例名,--reload 类似 nodemon 热重载
uvicorn main:app --reload
第一组对照表,先建立直觉:
| Express (Node) | FastAPI (Python) | 说明 |
|---|---|---|
const app = express() |
app = FastAPI() |
创建应用实例 |
app.get('/x', fn) |
@app.get("/x") |
注册路由(FastAPI 用装饰器) |
app.post / put / delete |
@app.post / .put / .delete |
HTTP 方法一一对应 |
req / res 对象 |
函数参数 / return 值 |
关键差异,见第三节 |
app.listen(3000) |
uvicorn main:app |
启动方式(外部服务器) |
手动 res.json(obj) |
直接 return dict |
自动序列化 |
二、边界一:装饰器即路由(@app.get 到底是什么)
Express 里注册路由是「调函数」:app.get(path, fn),把 fn 当参数传进去。FastAPI 里是「贴装饰器」:@app.get(path) 写在函数头顶上。两者效果一样,但写法不同,别被 @ 吓到。
装饰器机制详见第 10 篇。这里只需记住一个心智模型:
@app.get("/")就是「把下面这个函数登记到 app 的路由表里,绑定到 GET /」。如果你写过 Angular/TS 的@Component、@Get()(NestJS),这个语法你已经见过了——长得一模一样。
// Express 对照:同路径不同方法,链式或分开注册
app.get('/users', (req, res) => res.json([{ id: 1, name: 'Tom' }]))
app.post('/users', (req, res) => res.json({ ok: true }))
app.delete('/users/:user_id', (req, res) => res.json({ deleted: req.params.user_id }))
路径参数写法差异:Express 用冒号
:user_id,FastAPI 用花括号{user_id}。仅此而已。
三、边界二:没有 req/res——参数靠函数签名,响应靠 return
这是从 Express 转过来最大的认知转变,务必吃透。
Express 里,所有输入都从 req 这个大对象里掏(req.params / req.query / req.body),所有输出都往 res 上写(res.json / res.status)。FastAPI 反过来:你想要什么参数,就在函数签名里声明什么,FastAPI 看类型注解自动从对的位置取值、自动转类型。
// Express 对照:全从 req 掏,且全是字符串,要自己转类型
app.get('/users/:user_id', (req, res) => {
const userId = parseInt(req.params.user_id) // 手动转 int
const q = req.query.q || '' // 手动取 query、给默认值
const limit = parseInt(req.query.limit) || 10 // 手动转 + 默认
res.json({ user_id: userId, q, limit })
})
FastAPI 区分参数来源的规则(先记住前两条,body 见第四节):
| 参数特征 | FastAPI 判定为 | 对应 Express | 例子 |
|---|---|---|---|
出现在路径 {xxx} 里 |
路径参数 | req.params.xxx |
/users/{user_id} |
| 简单类型、不在路径里 | 查询参数 | req.query.xxx |
?q=tom&limit=5 |
| 类型是 Pydantic 模型 | 请求体 | req.body |
见第四节 |
✅ 这套「声明即获取」最大的好处:类型注解(详见第 11 篇)不再只是给编辑器看的提示,FastAPI 把它当成运行时的校验与转换规则。Express 里你写
req.query.limit永远是 string,要自己parseInt;FastAPI 写limit: int就真的拿到 int,转不动直接 422。这相当于 Express 里你得手动装 + 配一堆校验中间件才有的能力。
四、请求体:Pydantic 模型 ≈ TS interface + zod 二合一
POST/PUT 要收 JSON body 时,Express 里你装 body-parser、从 req.body 掏、再自己写校验。FastAPI 让你先定义一个 Pydantic 模型类,把它写进函数签名,body 的接收、解析、校验一步到位。
Pydantic 是 FastAPI 的数据校验基石,本篇只给最小用法,完整的字段约束、嵌套模型详见第 15 篇。
// Express + TS 对照:interface 只在编译期存在,运行时校验得另写(或上 zod)
interface UserIn {
name: string
age: number
email?: string
}
app.post('/users', (req, res) => {
const user = req.body as UserIn // 仅类型断言,运行时 body 可能根本不符!
// 想要真正的运行时校验,得手写 if,或引入 zod 单独定义 schema
if (typeof user.name !== 'string' || typeof user.age !== 'number') {
return res.status(422).json({ error: '参数不合法' })
}
res.json({ created: user.name, age: user.age })
})
⚠️ 关键差异:TS 的 interface 编译后就消失了,运行时拦不住一个乱传的 body——这是前端最容易误以为「类型已经保我了」的坑。Pydantic 模型是真实存在于运行时的对象,FastAPI 用它在请求进函数前就把关。所以 FastAPI 里你几乎不用写「参数校验 if」,这部分逻辑被模型吃掉了。
五、白送的接口文档:/docs(Express 要装 Swagger 才有)
把服务跑起来后,浏览器打开 http://127.0.0.1:8000/docs,你会看到一个自动生成的、可交互的 Swagger UI——所有路由、参数类型、请求体结构、能直接点「Try it out」发请求。
这不是额外配置出来的。FastAPI 把你写的类型注解 + Pydantic 模型,自动转成了 OpenAPI 规范并渲染成文档。
| 能力 | Express | FastAPI |
|---|---|---|
| 接口文档 | 装 swagger-jsdoc + 手写注释 |
内置,零配置,/docs 直接看 |
| 文档与代码同步 | 容易写完忘了更新 | 文档由代码生成,天然同步 |
| 在线调试 | 另开 Postman | /docs 里直接 Try it out |
对前端的实际价值:你转后端后写的接口,前端同学(或未来的你)打开
/docs就能看清参数和返回结构,不用再追着问「这个接口要传啥」。另有http://127.0.0.1:8000/redoc是另一种风格的只读文档。
六、async:语法和 JS 一模一样(机制差异留到后面)
FastAPI 的处理函数既可以是普通 def,也可以是 async def。如果你的函数里要 await 别的异步操作(查数据库、调外部 API),就写 async def。
// JS 对照:几乎逐行映射
app.get('/proxy', async (req, res) => {
const resp = await fetch('https://httpbin.org/get')
res.json(await resp.json())
})
边界提醒:现在你只需把
async/await当成「和 JS 写法一样」来用即可——能await的就async def,纯 CPU 计算或同步代码用普通def(FastAPI 会自动用线程池跑普通 def,不会卡住)。至于「Python 有 GIL、async 到底怎么并发的、和 Node 单线程有何不同」这些机制层面的东西,详见第 17 篇,本篇不展开,先建立「写法照搬 JS」的直觉就够了。
七、把一个 CRUD 接口串起来
综合前面所有要点,写一个最小但完整的用户接口(内存存储,数据库版详见第 16 篇):
四个接口跑起来后,去 /docs 就能直接点着测——这就是阶段三要求你能独立撸出的 CRUD 雏形。
八、总结
- 先给锚点:FastAPI 路由 ≈ Express 路由:最小可跑的一个接口,左边 Express、右边 FastAPI 并排看:
- 边界一:装饰器即路由(@app.get 到底是什么):Express 里注册路由是「调函数」:app.get(path, fn),把 fn 当参数传进去。
- 边界二:没有 req/res——参数靠函数签名,响应靠 return:这是从 Express 转过来最大的认知转变,务必吃透。
- 请求体:Pydantic 模型 ≈ TS interface + zod 二合一:Pydantic 是 FastAPI 的数据校验基石,本篇只给最小用法,完整的字段约束、嵌套模型详见第 15 篇。
- 白送的接口文档:/docs(Express 要装 Swagger 才有):另有 http://127.0.0.1:8000/redoc 是另一种风格的只读文档。
- async:语法和 JS 一模一样(机制差异留到后面):FastAPI 的处理函数既可以是普通 def,也可以是 async def。
学完自测
选择所有正确答案;提交后逐项核对判断依据。