介绍
FastAPI 底层 Web 框架就是 Starlette,FastAPI 在 Starlette 之上增加了 Pydantic、路由参数解析、OpenAPI文档等能力。
FastAPI 的请求、响应、异常处理、中间件、后台任务全部来自 Starlette。
官方仓库:https://github.com/encode/starlette
Starlette & FastAPI关系
| 功能 | Starlette | FastAPI |
|---|---|---|
| ASGI底层、Request/Response、中间件、异常、websocket、后台任务 | ✅ | ✅(直接复用) |
| 路径/查询参数解析 | ❌ | ✅ |
| Pydantic请求体校验 | ❌ | ✅ |
| 自动OpenAPI接口文档 | ❌ | ✅ |
FastAPI的 app 对象本质就是 Starlette 的子类。
from fastapi import FastAPI
print(issubclass(FastAPI, Starlette)) # True
一、定位
- Starlette:轻量异步 ASGI Web 框架,只做 Web 底层(路由、请求、响应、中间件、异常、生命周期)不做参数校验、不做接口文档。
- FastAPI = Starlette(web内核) + Pydantic(数据校验) + OpenAPI(接口文档)。
FastAPI 的 Request、Response、JSONResponse、HTTPException、BackgroundTasks 全部导入自 starlette,fastapi只是做了重新导出。
# 等价
from fastapi import Request
from starlette.requests import Request
from fastapi.responses import JSONResponse
from starlette.responses import JSONResponse
from fastapi import HTTPException
from starlette.exceptions import HTTPException
二、基础组件
1. 最小 Starlette 程序
import uvicorn
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
async def homepage(request: Request):
return JSONResponse({"msg":"hello starlette"})
routes = [
Route("/", homepage)
]
app = Starlette(routes=routes)
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
2. Request 对象(starlette.requests.Request)
常用属性
request.method # GET POST
request.url # URL对象
request.query_params # 查询参数,类字典 MultiDict
request.path_params # 路径参数
request.headers # 请求头 MultiDict
request.cookies # cookie
await request.body() # raw bytes
await request.json() # 解析json
await request.form() # form表单
Request 是ASGI scope + receive 封装,
await request.json()只能调用一次,body消费完就空。
3. Response 系列(starlette.responses)
Response(content, status_code, media_type)基础响应JSONResponse(content)json返回PlainTextResponse()纯文本HTMLResponse()htmlRedirectResponse(url)重定向FileResponse返回文件StreamingResponse流式返回(大文件、sse)
所有响应必须实现
__call__(scope, receive, send)ASGI协议。
三、Starlette 异常系统
starlette.exceptions.HTTPException
raise HTTPException(status_code=403, detail="禁止访问", headers={})
异常处理器 add_exception_handler
FastAPI 的 @app.exception_handler 本质就是封装 app.add_exception_handler()。
原生 Starlette 写法:
from starlette.applications import Starlette
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import JSONResponse
app = Starlette()
async def handler_403(request: Request, exc: HTTPException):
return JSONResponse({"code":403,"msg": exc.detail}, status_code=403)
# 注册异常处理器
app.add_exception_handler(HTTPException, handler_403)
底层逻辑
当路由内抛出异常,starlette 查找已注册异常处理器;找到就执行处理器返回Response;没有匹配就走内置默认处理器。
重要
- 异常处理器只在请求生命周期内生效
- BackgroundTasks、手动
asyncio.create_task()抛出异常,脱离request scope,不会触发异常处理器,直接抛出到事件循环。
Starlette 默认内置异常处理器
- 404 Not Found:找不到路由
- 405 Method Not Allowed:方法不允许
这些都是 Starlette 自带,FastAPI直接继承。
四、中间件 Middleware
Starlette中间件模型:ASGI包装器,接收 (request, call_next)。
FastAPI @app.middleware("http") 完全继承自 Starlette。
原生写法:
from starlette.middleware.base import BaseHTTPMiddleware
class LogMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
print(f"请求:{request.url}")
response = await call_next(request)
print(f"响应状态:{response.status_code}")
return response
app = Starlette(
routes=routes,
middleware=[
Middleware(LogMiddleware)
]
)
两种中间件写法:
- BaseHTTPMiddleware:面向对象,拿到Request/Response,简单常用
- 原始ASGI中间件:直接操作 scope, receive, send,性能最高,但繁琐
BaseHTTPMiddleware 有一个经典坑:不能读取 request.body(),会导致后续路由拿不到body数据。
五、BackgroundTasks 后台任务
starlette.background.BackgroundTasks
FastAPI直接导出这个类。
from starlette.background import BackgroundTasks
tasks = BackgroundTasks()
tasks.add_task(func, arg1, arg2)
# 附加到response,response返回客户端后才执行任务
response.background = tasks
关键点:
- 是同步/普通async函数,不是asyncio任务;
- 在响应发送给客户端之后才执行;
- 任务抛出异常 → 不会触发异常处理器,直接打印栈,请求已经结束;
- 只绑定在 Response 对象上生效。
注意区分:
- BackgroundTasks:starlette提供,请求结束后顺序执行;
- asyncio.create_task:创建真正asyncio任务,和请求生命周期无关。
六、生命周期 Lifespan
Starlette 0.20+ 使用 lifespan 上下文管理器,取代旧的 on_event("startup") / on_event("shutdown")。
FastAPI同样支持。
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: Starlette):
# 启动时执行
print("应用启动,初始化数据库、redis连接池")
yield
# 关闭时执行
print("应用关闭,释放资源")
app = Starlette(routes=routes, lifespan=lifespan)
@app.on_event("startup")在新版Starlette/FastAPI已经标记废弃,优先用 lifespan。
七、路由系统 Routing
Route(path, endpoint)普通http路由WebSocketRoute(path, endpoint)websocket路由Mount(path, app)挂载子ASGI应用(非常常用,静态文件、子应用)
示例挂载静态文件:
from starlette.staticfiles import StaticFiles
routes = [
Route("/", homepage),
Mount("/static", app=StaticFiles(directory="./static"), name="static")
]