入门
第一个 API
FastAPI 基于 Starlette 和 Pydantic。uvicorn 是 ASGI 服务器。--reload 用于在开发期间自动重载。
# install FastAPI and uvicorn
pip install fastapi uvicorn
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
# run: uvicorn main:app --reload使用 Uvicorn 运行
应用实例 'main:app' 表示模块 'main' 中的变量 'app'。--reload 仅用于开发;生产环境中不要使用,应改用进程管理器。
# development with auto-reload
uvicorn main:app --reload
# specify host and port
uvicorn main:app --host 0.0.0.0 --port 8000
# run programmatically
import uvicorn
if __name__ == "__main__":
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)异步路径操作
路径操作可以用 'def'(在线程池中运行)或 'async def' 声明。当函数通过异步库执行 I/O 时使用 async def;在 async def 中混用阻塞调用会阻塞事件循环。
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"message": "hello"}
@app.get("/sync")
def read_sync():
return {"message": "sync also works"}
# use async when calling async libs (httpx, databases)
@app.get("/data")
async def fetch_data():
import httpx
async with httpx.AsyncClient() as client:
r = await client.get("https://api.example.com")
return r.json()路径操作装饰器
FastAPI 支持所有 HTTP 方法:GET、POST、PUT、PATCH、DELETE、OPTIONS、HEAD。每个方法对应一个装饰器。在装饰器上使用 status_code= 可覆盖默认的成功状态码。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items")
def list_items(): ...
@app.post("/items")
def create_item(): ...
@app.put("/items/{item_id}")
def replace_item(item_id: int): ...
@app.patch("/items/{item_id}")
def update_item(item_id: int): ...
@app.delete("/items/{item_id}")
def delete_item(item_id: int): ...
@app.options("/items")
def options_items(): ...
@app.head("/items")
def head_items(): ...JSON 响应与状态码
返回 dict/list 会自动转换为 JSON,状态码为 200(或你指定的 status_code)。若需完全控制状态码、头部或内容,请直接返回 JSONResponse 或 Response。
from fastapi import FastAPI, status
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/plain")
def plain():
return {"key": "value"} # auto JSON, 200 OK
@app.post("/created", status_code=status.HTTP_201_CREATED)
def created():
return {"id": 1}
@app.get("/custom")
def custom():
return JSONResponse(
status_code=418,
content={"error": "I'm a teapot"},
headers={"X-Custom": "yes"},
)项目结构
使用 APIRouter 将不断增长的应用拆分为模块,然后通过 app.include_router() 注册。将 Pydantic 模型、数据库设置和依赖项放在单独的文件中以便维护。
myapp/
main.py # creates the FastAPI app
routers/
users.py # APIRouter for /users
items.py # APIRouter for /items
models/
schemas.py # Pydantic models
database.py # DB engine & session
dependencies.py # shared Depends functions
tests/
test_main.py
# main.py
from fastapi import FastAPI
from routers import users, items
app = FastAPI()
app.include_router(users.router)
app.include_router(items.router, prefix="/items")路径参数
基本路径参数
从路径中捕获的值默认是字符串,除非添加类型注解。路径中的参数名必须与函数参数名完全匹配。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id):
return {"item_id": item_id}
# GET /items/42 -> {"item_id": "42"} (string by default)类型转换
添加类型注解(int、float、bool、str、Enum)后,FastAPI 会验证并转换路径值。无效值返回 422 响应和清晰的错误信息。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
# GET /items/3 -> {"item_id": 3}
# GET /items/abc -> 422 Validation Error (not an integer)路径验证
Path() 为路径参数添加元数据和约束。数值约束:ge (>=)、gt (>)、le (<=)、lt (<)。路径参数总是必填的,因此 Path() 不能设置默认值。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(
item_id: int = Path(
title="The ID of the item",
description="Must be a positive integer",
ge=1,
le=1000,
),
):
return {"item_id": item_id}数值约束
对 int 或 float 路径参数使用 ge/gt/le/lt 来强制数值范围。组合约束可提供精确的边界验证并自动返回 422 错误。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/ratio/{value}")
def ratio(
value: float = Path(gt=0, lt=1), # 0 < value < 1
):
return {"value": value}
@app.get("/page/{page}")
def page(
page: int = Path(ge=1), # >= 1
size: int = Path(le=100), # <= 100
):
return {"page": page, "size": size}路由顺序很重要
路由按声明顺序匹配。在动态模式(如 /users/{user_id})之前声明具体路径(如 /users/me),否则动态路由会遮蔽具体路由。
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/me")
def read_current_user():
return {"user": "me"}
@app.get("/users/{user_id}")
def read_user(user_id: str):
return {"user_id": user_id}
# /users/me MUST be declared before /users/{user_id}
# otherwise 'me' would match the {user_id} pattern枚举路径参数
使用 Enum 类型作为路径参数可将其限制为预定义值,并在 OpenAPI 文档中生成枚举 schema。继承 (str, Enum) 使值序列化为字符串。
from enum import Enum
from fastapi import FastAPI
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
app = FastAPI()
@app.get("/models/{model_name}")
def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model": model_name, "layers": 5}
return {"model": model_name}
# GET /models/resnet -> {"model": "resnet"}查询参数
基本查询参数
不在路径中的函数参数会成为查询参数。提供默认值使其变为可选;当查询键缺失时使用默认值。
from fastapi import FastAPI
app = FastAPI()
fake_items = [{"item": "a"}, {"item": "b"}, {"item": "c"}]
@app.get("/items")
def list_items(skip: int = 0, limit: int = 10):
return fake_items[skip : skip + limit]
# GET /items?skip=0&limit=2
# GET /items -> skip=0, limit=10可选查询参数
使用 Optional[str] = None(或 Python 3.10+ 的 str | None = None)表示可省略的查询参数。必填参数只需不设默认值。
from typing import Optional
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: str, q: Optional[str] = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
# GET /items/1?q=hello
# GET /items/1 (q is None)查询验证
Query() 添加验证:字符串用 min_length/max_length,数字用 ge/gt/le/lt,字符串格式用 pattern(正则)。使用 default= 设置值的同时应用约束。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/search")
def search(q: str = Query(min_length=3, max_length=50, pattern="^[a-zA-Z]+$")):
return {"q": q}
@app.get("/page")
def page(limit: int = Query(default=10, ge=1, le=100)):
return {"limit": limit}布尔值转换
布尔查询参数接受多种真值:true、1、yes、on(不区分大小写)。其他值被解释为 false。无效类型如 ?active=maybe 返回 422。
from fastapi import FastAPI
app = FastAPI()
@app.get("/flags")
def flags(active: bool = False):
return {"active": active}
# GET /flags?active=true -> {"active": true}
# GET /flags?active=1 -> {"active": true}
# GET /flags?active=yes -> {"active": true}
# GET /flags?active=off -> {"active": false}列表查询参数
List[str] 参数接受查询字符串中重复的相同键。使用 Query(default=[]) 默认为空列表,或 Query(default=None) 允许缺失值。
from typing import List
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items")
def list_items(q: List[str] = Query(default=[])):
return {"q": q}
# GET /items?q=a&q=b&q=c -> {"q": ["a", "b", "c"]}
# declare default=None to accept a list or null必填查询参数
使用 Query(...)(省略号)声明一个仍带验证元数据的必填查询参数。没有默认值且没有 Query(...) 的普通注解参数也是必填的。
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items")
def read_item(q: str = Query(...)):
return {"q": q}
# GET /items?q=hello -> ok
# GET /items -> 422 (q is required)
# Query(...) (Ellipsis) marks a parameter as required请求体(Pydantic)
BaseModel 基础
声明为函数参数的 Pydantic BaseModel 成为 JSON 请求体。FastAPI 验证载荷、转换类型,无效数据返回 422。带默认值的字段是可选的。
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
in_stock: bool = True
app = FastAPI()
@app.post("/items")
def create_item(item: Item):
return item
# POST /items body: {"name": "Apple", "price": 0.5}
# -> {"name": "Apple", "price": 0.5, "in_stock": true}字段类型与约束
Field() 为模型属性添加约束和元数据:字符串用 min_length/max_length,数字用 gt/ge/lt/le,可变默认值用 default_factory。model_config 的 json_schema_extra 为文档添加示例。
from pydantic import BaseModel, Field
from typing import Optional
class Item(BaseModel):
name: str = Field(min_length=1, max_length=100)
price: float = Field(gt=0, description="must be positive")
tax: Optional[float] = None
tags: list[str] = Field(default_factory=list)
model_config = {
"json_schema_extra": {
"examples": [{"name": "Apple", "price": 0.5, "tags": ["fruit"]}]
}
}嵌套模型
模型可以嵌套:将另一个 BaseModel 声明为字段类型。FastAPI 递归验证整个嵌套结构,生成的 OpenAPI schema 也反映嵌套对象。
from pydantic import BaseModel
from typing import Optional
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: Optional[str] = None
image: Optional[Image] = None
images: list[Image] = []
# valid body:
# {"name": "Phone", "image": {"url": "http://x/a.png", "name": "a"}}字段验证
使用 @field_validator(Pydantic v2)为每个字段添加自定义验证逻辑。抛出 ValueError 可拒绝输入(FastAPI 将其转为 422)。返回(可能转换后的)值以保留它。
from pydantic import BaseModel, field_validator
class User(BaseModel):
name: str
email: str
@field_validator("email")
@classmethod
def email_must_contain_at(cls, v: str) -> str:
if "@" not in v:
raise ValueError("must contain @")
return v.lower()
@field_validator("name")
@classmethod
def name_stripped(cls, v: str) -> str:
return v.strip()可选字段与默认值
只有没有默认值且不是 Optional 的字段才是必填的。Optional[str] = None 是可选可空字段的标准模式;像 0.0 这样的普通默认值使字段可选并使用该值。