4、FastAPI入门:FastAPI安装与Hello World、路径参数与查询参数、请求体与响应模型

好,咱们开始进入FastAPI的世界。

说实话,我最早接触FastAPI是在一个紧急的API重构项目里。当时Django太重,Flask又缺类型校验,折腾得够呛。后来试了试FastAPI,嗯,真香。它把Python的类型提示玩出了花,性能还直追Go写的框架。今天咱们就从零开始,把FastAPI的底子打扎实。

4.1 安装FastAPI

安装其实很简单,一行命令搞定。但我建议你最好在虚拟环境里操作,别污染全局的Python环境。

pip install fastapi
pip install uvicorn  # 这是ASGI服务器,用来跑FastAPI应用

我个人习惯把常用依赖写进 requirements.txt

fastapi==0.104.1
uvicorn==0.24.0

然后执行 pip install -r requirements.txt 就行。

小提示: 如果你在Windows上遇到编码问题,可以加个 python-dotenv 包,方便管理环境变量。我在项目中经常这么干。

4.2 Hello World:第一个FastAPI应用

来,写个最简单的接口。新建一个 main.py

from fastapi import FastAPI

app = FastAPI()

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

启动命令:

uvicorn main:app --reload

打开浏览器访问 http://127.0.0.1:8000,你会看到:

{"message": "Hello World"}

是不是很清爽?--reload 参数让代码修改后自动重启,开发时特别方便。不过生产环境千万别加这个参数,我见过有人上线忘了去掉,结果服务器CPU飙到100%。

重点: FastAPI会自动生成交互式API文档。访问 /docs 就能看到Swagger UI,访问 /redoc 能看到ReDoc。这个特性在前后端联调时简直救命。

4.3 路径参数

路径参数就是从URL路径中提取变量。比如你想根据用户ID查信息:

@app.get("/users/{user_id}")
def get_user(user_id: int):
    return {"user_id": user_id, "name": f"User_{user_id}"}

这里 user_id 被声明为 int 类型。如果你传一个非数字的值,比如 /users/abc,FastAPI会自动返回422错误。为什么?因为它内置了Pydantic的校验机制。

我曾经在项目中遇到过一个问题:路径参数和查询参数搞混了。比如 /items/{item_id}/items?item_id=123 是两回事。路径参数是URL结构的一部分,查询参数是 ? 后面的键值对。别搞反了。

路径参数还支持枚举类型:

from enum import Enum

class UserType(str, Enum):
    admin = "admin"
    user = "user"
    guest = "guest"

@app.get("/users/{user_type}/{user_id}")
def get_user_by_type(user_type: UserType, user_id: int):
    return {"type": user_type, "id": user_id}

这样 /users/admin/123 能正常访问,但 /users/superadmin/123 就会报错。嗯,这里要注意:枚举值必须严格匹配。

4.4 查询参数

查询参数就是URL中 ? 后面的部分。比如分页、过滤条件:

@app.get("/items/")
def list_items(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

访问 /items/?skip=0&limit=5,返回:

{"skip": 0, "limit": 5}

查询参数可以有默认值,也可以声明为可选:

from typing import Optional

@app.get("/items/")
def list_items(
    skip: int = 0,
    limit: int = 10,
    q: Optional[str] = None  # 可选参数
):
    result = {"skip": skip, "limit": limit}
    if q:
        result["q"] = q
    return result

你想想看,如果 q 没传,它就是 None,不会报错。这种设计在搜索接口里特别常见。

避坑指南: 我曾经把查询参数和路径参数写重名了,结果FastAPI报了个奇怪的错误。记住:路径参数和查询参数的名字不能冲突,否则框架会混淆。

4.5 请求体与响应模型

请求体就是POST、PUT等请求中携带的JSON数据。FastAPI用Pydantic模型来定义请求体结构:

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float
    is_offer: Optional[bool] = None

@app.post("/items/")
def create_item(item: Item):
    return {"item_name": item.name, "item_price": item.price}

发送POST请求时,请求体必须是这样的JSON:

{
    "name": "笔记本电脑",
    "price": 5999.00,
    "is_offer": true
}

如果 is_offer 不传,它默认为 None。如果传了类型不对的值,比如 price: "免费",FastAPI会直接返回422错误,告诉你字段类型不匹配。

响应模型就更厉害了。你可以定义返回的数据结构,FastAPI会自动过滤掉多余字段:

class ItemResponse(BaseModel):
    name: str
    price: float
    # 注意:这里没有 is_offer

@app.post("/items/", response_model=ItemResponse)
def create_item(item: Item):
    # 假设数据库里存了更多字段
    db_item = {
        "name": item.name,
        "price": item.price,
        "is_offer": item.is_offer,
        "internal_code": "SECRET_123"  # 这个字段不会返回
    }
    return db_item

返回给客户端的数据只包含 namepriceinternal_code 被自动过滤掉了。说白了,响应模型就是一道防火墙,防止你泄露敏感字段。

实战经验: 我在做支付接口时,响应模型帮了大忙。数据库里存了银行卡号后四位,但返回给前端时只返回掩码后的字符串。用响应模型做字段映射,既安全又省事。

4.6 综合示例:一个完整的CRUD雏形

把上面学的东西串起来,写个简单的物品管理接口:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional, List

app = FastAPI()

# 模拟数据库
items_db = {}

class Item(BaseModel):
    name: str
    price: float
    is_offer: Optional[bool] = None

class ItemResponse(BaseModel):
    id: int
    name: str
    price: float

@app.post("/items/", response_model=ItemResponse)
def create_item(item: Item):
    item_id = len(items_db) + 1
    items_db[item_id] = item
    return ItemResponse(id=item_id, name=item.name, price=item.price)

@app.get("/items/{item_id}", response_model=ItemResponse)
def get_item(item_id: int):
    if item_id not in items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    item = items_db[item_id]
    return ItemResponse(id=item_id, name=item.name, price=item.price)

@app.get("/items/")
def list_items(skip: int = 0, limit: int = 10):
    all_items = [
        ItemResponse(id=i, name=item.name, price=item.price)
        for i, item in items_db.items()
    ]
    return all_items[skip: skip + limit]

这个例子虽然简单,但包含了路径参数、查询参数、请求体、响应模型、错误处理等核心概念。你可以在 /docs 里直接测试每个接口,非常直观。

我的建议: 刚开始学FastAPI时,多利用 /docs 页面。它不仅能测试接口,还能看到每个参数的校验规则。比Postman还方便。

好了,FastAPI的入门就到这里。下一章咱们会深入依赖注入和中间件,那才是FastAPI真正发力的地方。记住:先跑通Hello World,再理解路径和查询参数的区别,最后用Pydantic模型把请求和响应管起来。这三步走稳了,后面的路就好走了。