Skip to content

第一个 FastAPI 应用:从零到运行 ​

实验目标 ​

创建并运行一个完整的 FastAPI 应用,理解启动流程、路径操作、自动文档。

环境准备 ​

bash
# 创建虚拟环境
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# 安装依赖
pip install fastapi uvicorn[standard]

实验 1:Hello World ​

创建 main.py:

python
from fastapi import FastAPI

app = FastAPI()

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

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

运行:

bash
uvicorn main:app --reload

测试:

bash
# 根路径
curl http://localhost:8000/
# 输出: {"message":"Hello World"}

# 路径参数
curl http://localhost:8000/items/42
# 输出: {"item_id":42,"q":null}

# 查询参数
curl http://localhost:8000/items/42?q=test
# 输出: {"item_id":42,"q":"test"}

# 类型错误
curl http://localhost:8000/items/foo
# 输出: 422 错误

实验 2:请求体与验证 ​

python
from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str = Field(..., min_length=1, max_length=50)
    description: str = None
    price: float = Field(..., gt=0)
    tax: float = None

@app.post("/items/")
async def create_item(item: Item):
    item_dict = item.dict()
    if item.tax:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict

测试:

bash
# 正确请求
curl -X POST http://localhost:8000/items/ \
  -H "Content-Type: application/json" \
  -d '{"name":"Laptop","price":1200.00,"tax":120.00}'

# 输出: {"name":"Laptop","description":null,"price":1200.0,"tax":120.0,"price_with_tax":1320.0}

# 验证错误:price 为负数
curl -X POST http://localhost:8000/items/ \
  -H "Content-Type: application/json" \
  -d '{"name":"Laptop","price":-100}'

# 输出: 422 错误,说明 price 必须大于 0

实验 3:查看自动文档 ​

  1. Swagger UI:访问 http://localhost:8000/docs

    • 交互式测试界面
    • 自动生成的 API 文档
    • 可以直接在浏览器测试 API
  2. ReDoc:访问 http://localhost:8000/redoc

    • 更清晰的文档视图
    • 适合分享给前端团队
  3. OpenAPI JSON:访问 http://localhost:8000/openapi.json

    • 原始 OpenAPI 规范
    • 可用于生成客户端代码

实验 4:响应模型 ​

python
from typing import Optional

class ItemResponse(BaseModel):
    name: str
    price: float
    price_with_tax: Optional[float] = None

@app.post("/items/", response_model=ItemResponse)
async def create_item(item: Item):
    # 即使返回额外字段,也只序列化 response_model 定义的字段
    return {
        "name": item.name,
        "price": item.price,
        "price_with_tax": item.price + (item.tax or 0),
        "internal_field": "这个不会出现在响应中"
    }

实验 5:状态码 ​

python
from fastapi import status, HTTPException

@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(item: Item):
    return item

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    return items[item_id]

验收标准 ​

✅ 应用成功启动,访问 http://localhost:8000 返回 Hello World
✅ 路径参数和查询参数正确工作
✅ 请求体验证失败返回 422 错误
✅ /docs 和 /redoc 可访问且显示完整文档
✅ 响应模型过滤了多余字段
✅ 状态码和异常处理正确

常见问题 ​

Q: uvicorn 找不到 app

bash
# 确保文件名是 main.py,且代码中有 app = FastAPI()
# 或明确指定:uvicorn filename:app

Q: 自动重载不工作

bash
# 确保使用 --reload 标志
uvicorn main:app --reload

Q: 端口已被占用

bash
# 更改端口
uvicorn main:app --port 8001

下一步:进入调试与边界学习错误处理。

用心记录每一步成长