现代 Python Web 框架:FastAPI实战指南

FastAPI 实战指南:手把手带你从零搭建优雅的 RESTful 服务

来源:CSDN

作者:燐妤

原文链接:点击此处


目录

[一、前言:为什么选择 FastAPI](#一、前言:为什么选择 FastAPI)

二、环境准备与第一个接口

[三、路径参数:让 URL 动起来](#三、路径参数:让 URL 动起来)

四、查询参数:灵活的可选输入

[五、请求体:用 Pydantic 模型拥抱 JSON](#五、请求体:用 Pydantic 模型拥抱 JSON)

[六、自动生成的 API 文档:Swagger & ReDoc](#六、自动生成的 API 文档:Swagger & ReDoc)

七、模块化路由:告别单文件噩梦

[八、直接拿 Request 对象:更自由的数据获取](#八、直接拿 Request 对象:更自由的数据获取)

[九、回归本质:RESTful 设计的最佳实践](#九、回归本质:RESTful 设计的最佳实践)

十、总结与踩坑指南


一、前言:为什么选择 FastAPI

去年团队决定把一部分旧 Flask 服务重写,技术选型时组长把 FastAPI 推到了桌面上。我当时心想:"又是个网红框架,能比 Flask 好到哪去?" 直到我写完第一个接口,看到 Swagger UI 自动蹦出来,所有请求参数和返回格式一目了然,不用再手动写接口文档,也不用担心前端哥传错参数类型,我才真香了。

FastAPI 是 2018 年出生的现代 Python Web 框架,它专门为 API 而生,底层依赖两个明星库:Starlette(负责网络层,异步高性能)和 Pydantic(数据验证,利用类型注解)。只要你的 Python 版本 ≥ 3.8,一条命令就能开跑。它最打动开发者的四个点:

  1. 快:异步处理,性能比肩 Node.js 和 Go,压测数据很能打。
  2. 自动文档:代码写完,/docs/redoc 两个界面直接生成,支持在线测试。
  3. 类型提示驱动:写 Python 类型注解就能实现参数校验,IDE 智能提示也很舒服。
  4. 原生异步:async/await 写起来就像同步代码,特别适合 IO 密集场景(调数据库、请求微服务等)。

如果你是做前后端分离、微服务、或者需要处理大量 JSON 的接口,FastAPI 会让你的开发体验上一个台阶。


二、环境准备与第一个接口

先装上两个库,一台机器上搞定开发环境:

Bash

python 复制代码
pip install fastapi
pip install "uvicorn[standard]"

uvicorn 是 ASGI 服务器,注意是 ASGI 不是 Flask 用的 WSGI。ASGI 天生支持异步并发,WSGI 则是一把梭的同步模型。FastAPI 应用就用 uvicorn 跑,生产环境还可以用 hypercorn

接下来写一个 main.py

Python

python 复制代码
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/hello/{name}")
async def say_hello(name: str):
    # 路径参数自动传给 name
    return {"message": f"Hello {name}"}

案例演示:

启动方式特别简单,在终端里敲:

Bash

python 复制代码
uvicorn main:app --reload
  • main 是文件名(main.py
  • app 是 FastAPI 实例的变量名
  • --reload 代表代码有变动自动重启,只建议开发用

或者你可以在代码里直接写启动语句,这样右键运行也更方便:

Python

python 复制代码
import uvicorn

if __name__ == "__main__":
    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)

效果演示:

打开浏览器访问 http://127.0.0.1:8000,你就能看到 {"message":"Hello World"}。再试试 /hello/CSDN,输出 {"message":"Hello CSDN"}

常用 HTTP 方法装饰器一览,都是 RESTful 里的老面孔:

装饰器 用途
@app.get() 获取资源
@app.post() 创建资源
@app.put() 全量更新资源
@app.patch() 部分更新资源
@app.delete() 删除资源

三、路径参数:让 URL 动起来

路径参数就是把值嵌在 URL 路径里,比如 /items/100 里的 100 就是动态的商品 ID。

Python

python 复制代码
@app.get("/items/{item_id}")
async def read_item(item_id: int):
    # 注意类型声明为 int,FastAPI 会自动把字符串"100"转成整数
    return {"item_id": item_id}

如果传的不是数字,比如 /items/abc,FastAPI 直接返回 422 校验错误,不用你写任何判断。这就是类型提示的威力。

常见误区:路径参数如果类型写错,比如后台期望 int 却传了 str,开发时直接报错,线上会返回 422。所以养成声明类型的习惯,否则接口容错性会很差。


四、查询参数:灵活的可选输入

查询参数就是 URL 里 ? 后面的部分,比如 /items/100?q=python&page=2。在 FastAPI 里,只要函数参数不是路径参数,它就会自动解释成查询参数。

Python

python 复制代码
from typing import Union

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: Union[str, None] = None, page: int = 1):
    # q 是可选的查询参数,默认 None
    # page 默认1
    return {"item_id": item_id, "q": q, "page": page}

这里的 Union[str, None] 表示 q 要么是字符串,要么是 None。Python 3.10+ 可以直接写 str | None。如果不给 q 传值,它就用默认值 None,接口照样正常工作。

为什么这么设计? 让调用方可以按需筛选、分页,服务端不用写一堆 if 判断参数是否存在。当你要做搜索、过滤功能时,查询参数是最自然的选择。


五、请求体:用 Pydantic 模型拥抱 JSON

光有 URL 参数还不够,创建用户、提交订单这些操作需要传一个 JSON 对象。FastAPI 结合 Pydantic 的 BaseModel,把请求体定义得明明白白。

Python

python 复制代码
from pydantic import BaseModel
from typing import Union

class Item(BaseModel):
    name: str
    price: float
    is_offer: Union[bool, None] = None   # 可选字段

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    # item 会自动从请求体 JSON 解析成 Item 实例
    # 如果传过来的 JSON 缺少 name 或 price,会直接 422 报错
    return {"item_name": item.name, "item_id": item_id, "offer": item.is_offer}

用 Postman 或 Swagger 发一个 PUT 请求到 /items/123,请求体:

JSON

python 复制代码
{
    "name": "《FastAPI实战》",
    "price": 59.9,
    "is_offer": true
}

FastAPI 在背后做的事:

  • JSON 字符串 → Python 字典 → Item 对象
  • 检查 name 必须是字符串,price 必须是浮点数
  • is_offer 缺了也没事,默认 None
  • 如果 price 传了 "abc",直接返回 422 并告诉你错在哪

嵌套模型也非常简单,比如用户有收货地址:

Python

python 复制代码
class Address(BaseModel):
    city: str
    street: str

class User(BaseModel):
    name: str
    age: int
    address: Address

这样前端传来的 JSON 再复杂也能被结构化校验,配合自动生成的文档,前后端沟通成本直线下降。


六、自动生成的 API 文档:Swagger & ReDoc

启动服务后,直接访问 http://127.0.0.1:8000/docs,你会看到一个 Swagger UI 界面,上面列出了所有接口,点开就能输入参数并发送请求。另一个 http://127.0.0.1:8000/redoc 则是更偏向阅读的文档风格。

这套文档是基于 OpenAPI 规范自动生成的,你代码里写的参数类型、Pydantic 模型、默认值全部变成了 JSON Schema。你甚至不用写一行文档注释,对于接口数量多、频繁变更的项目简直是救命稻草。

实际开发经验:我习惯在调试阶段一直开着 /docs,参数校验失败时直接看错误提示,比看终端日志直观。


七、模块化路由:告别单文件噩梦

当一个系统有十几个接口时,全塞在 main.py 里会非常难维护。FastAPI 提供了 APIRouter 来实现路由拆分,按业务模块组织代码。

假设项目结构:

project/ main.py api/ __init__.py books.py users.py

books.py 示例:

Python

python 复制代码
from fastapi import APIRouter

router = APIRouter()

@router.get("/books")
async def get_books():
    return {"books": ["Python入门", "FastAPI深入"]}

@router.post("/books")
async def create_book(name: str):
    # 实际开发会存数据库,这里只演示
    return {"msg": f"创建书籍:{name}"}

users.py 类似,用不同的路由前缀。

然后在 main.py 里统一注册:

Python

python 复制代码
from fastapi import FastAPI
from api.books import router as book_router
from api.users import router as user_router

app = FastAPI()

app.include_router(book_router, prefix="/book", tags=["书籍管理"])
app.include_router(user_router, prefix="/user", tags=["用户管理"])

现在访问 /book/books 就是获取书籍列表,/user/... 则是用户相关。tags 参数会让它们在 Swagger 里分组显示,干净利落。

为什么这么设计? 随着团队人数变多,每个人负责不同模块,拆分路由可以避免 Git 冲突,也让单元测试更容易编写。大型项目还可以进一步按版本分组,比如 prefix="/v1"


八、直接拿 Request 对象:更自由的数据获取

大部分时候我们使用参数声明就够,但有些场景需要读取请求头、客户端 IP、原始请求体,这时可以直接注入 Request 对象。

Python

python 复制代码
from fastapi import FastAPI, Request

@app.get("/ip")
async def get_ip(request: Request):
    client_host = request.client.host
    user_agent = request.headers.get("user-agent")
    return {"ip": client_host, "ua": user_agent}

@app.post("/raw")
async def raw_body(request: Request):
    body = await request.body()           # 原始字节
    json_data = await request.json()      # 解析成 dict
    return {"raw_length": len(body), "parsed": json_data}

常用操作速查:

  • request.query_params ------ 拿到 QueryParams 对象,可转字典
  • await request.json() ------ 异步取值,用于 POST/PUT 这类
  • request.headers ------ 请求头字典
  • request.client.host ------ 客户端 IP,做频率限制时会用到
  • await request.form() ------ 处理表单上传

注意事项:

  1. request.json() 只能调用一次,因为流读完后指针就到底了。如果想同时记录日志和交给 Pydantic 模型,要么用依赖注入,要么把请求体先读出来再传递。
  2. 如果你已经用 Pydantic 模型声明了参数,FastAPI 内部已经读了请求体,你就不能再次调用 request.json(),会报错。此时可以用 Body 或依赖获取原始数据。

九、回归本质:RESTful 设计的最佳实践

虽然 FastAPI 功能强大,但接口设计依旧要遵循 RESTful 规范,否则容易写出"伪 REST"。

方法 含义 幂等性 示例
GET 获取资源 GET /users/1
POST 创建资源 POST /users
PUT 全量更新 PUT /users/1
PATCH 部分更新 PATCH /users/1
DELETE 删除资源 DELETE /users/1

强调两点:

  • 幂等性意味着多次相同请求的结果与一次相同,GET/PUT/DELETE 都应保证。
  • 前后端分离模式下,后端只返回 JSON 数据,前端(Vue、React 等)负责渲染,绝不返回 HTML 模板。

FastAPI 天然适合这种模式,你只管把 JSON 返回写清楚,让前端同事拿着 /docs 去联调,再也不需要吼:"接口文档更新了吗!"


十、总结与踩坑指南

一路下来,你会发现 FastAPI 几乎把开发 API 的琐碎事全包了:类型校验、文档生成、异步处理、模块化路由。记忆时可以用一句口诀:Starlette 底座 + Pydantic 校验 + 类型提示 = 自动文档。

动手实践时注意这些容易翻车的点:

  • 路径参数类型:一定要明确 intstr,否则可能匹配到错误的路由。
  • 请求体复用:同一个 Pydantic 模型如果用于多个接口,后期改字段要小心影响面,推荐用继承或独立模型。
  • 依赖注入:Depends 是 FastAPI 的精髓,复用权限校验、数据库连接时能省很多代码,可以慢慢深入。
  • 异常处理:用 HTTPException 返回明确的状态码和错误信息,而不是直接抛 500。
  • CORS 跨域:前后端分离时,务必在 main.py 里添加 CORSMiddleware,否则前端请求会报跨域错误。

最后建议大家新建一个虚拟环境,把今天所有代码跑一遍,再用 /docs 把每个接口测一测。有任何问题,欢迎在评论区讨论,我会逐一回复。原创不易,如果觉得有帮助,记得点赞收藏支持一下~

每日励志文案:

我爱我缓慢向上的勇气

------《人名日报》

相关推荐
清风一徐1 小时前
Python函数基础
开发语言·python
花落yu1 小时前
【无标题】
pytorch·python·深度学习
zhangfeng11331 小时前
htc 中minconda 明明安装了 Python 3.10显示 python 3.8 因为 `conda activate` 没有真正切换成功
开发语言·python·conda
m沐沐1 小时前
【机器学习】NLP---用 Python+TF-IDF 给《红楼梦》自动提取关键词
人工智能·python·机器学习·自然语言处理·nlp·中文分词·tf-idf
Fleshy数模1 小时前
深度学习核心:神经网络
python
m沐沐1 小时前
【机器学习】Python 实现垃圾邮件分类(随机森林 + 可视化 + 特征重要性)
人工智能·python·随机森林·机器学习·分类·pycharm·回归算法
在繁华处1 小时前
Java从零到熟练(八):泛型与注解
java·开发语言·python
SilentSamsara1 小时前
命令行工具开发:Click/Typer + 打包为独立二进制
linux·服务器·开发语言·前端·python·青少年编程·fastapi