术→技巧, 研发

FastAPI学习之Starlette

钱魏Way · · 35 次浏览

FastAPI与Starlette的关系

FastAPI 和 Starlette 是密切相关的 Python Web 框架,理解它们的关系有助于更好地选择和使用工具。

基本定义

  • Starlette
    • 轻量级 ​ASGI​(Asynchronous Server Gateway Interface)框架。
    • 提供基础的 HTTP 请求/响应处理、路由、中间件、WebSocket 支持等。
    • 核心代码约 3,000 行,无强制依赖。
  • FastAPI
    • 基于 Starlette 构建的高性能 Web 框架。
    • 在 Starlette 基础上添加了 ​数据验证​(通过 Pydantic)、依赖注入自动 API 文档生成​(Swagger/Redoc)等高级功能。

核心关系

  • 继承关系
    • FastAPI 直接继承 Starlette 的Router 和 APIRoute,所有 Starlette 的功能在 FastAPI 中可用。
    • FastAPI 的Request 和 Response 对象直接来自 Starlette。
  • 扩展功能
# FastAPI 扩展 Starlette 的示例
from fastapi import FastAPI
app = FastAPI()  # 继承自 Starlette 的 Starlette 类

# 直接使用 Starlette 的中间件
from starlette.middleware.cors import CORSMiddleware
app.add_middleware(CORSMiddleware, allow_origins=["*"])

功能对比

​功能 ​Starlette ​FastAPI
路由与请求处理 ✅(继承并扩展)
WebSocket 支持
中间件系统 ✅(完全兼容)
数据验证(请求/响应) ✅(基于 Pydantic)
自动 API 文档 ✅(Swagger/Redoc 自动生成)
依赖注入 ✅(复杂依赖树支持)
异步支持 ✅(ASGI)

  • FastAPI = Starlette + Pydantic + 高级功能​
  • 若需要数据验证、依赖注入和自动化文档,优先选 FastAPI;若追求极简或深度定制,直接使用 Starlette。两者均可无缝协作。

性能对比

  • 异步非阻塞:基于 async/await 语法,轻松处理 10K+ 并发连接
  • 高效协议处理:内置 HTTP/1.1、HTTP/2、WebSocket 协议解析
  • 底层性能一致:FastAPI 的性能几乎与 Starlette 相同,因为其核心依赖 Starlette 的异步处理。
  • 测试数据​(每秒请求数):
    • Flask (WSGI): ~1,200 req/s
    • Starlette(ASGI): ~15,000 requests/s
    • FastAPI(基于 Starlette): ~14,900 requests/s(加入数据验证后仅有微小损耗)

使用场景

​选择 Starlette 的情况

  • 需要极简的 ASGI 框架。
  • 开发高度定制化的中间件或协议(如自定义 WebSocket 逻辑)。
  • 项目对依赖项大小敏感(Starlette 无强制依赖)。

​选择 FastAPI 的情况

  • 快速构建 RESTful API 或 Web 服务。
  • 需要自动验证请求数据(如 JSON、表单)并生成文档。
  • 复杂业务逻辑需要依赖注入解耦。

代码示例对比

Starlette 实现简单 API

from starlette.applications import Starlette
from starlette.responses import JSONResponse

app = Starlette()

@app.route("/")
async def homepage(request):
    return JSONResponse({"message": "Hello Starlette!"})

FastAPI 实现相同功能(带数据验证)​

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Message(BaseModel):
    text: str

@app.post("/")
async def send_message(msg: Message):
    return {"response": f"Received: {msg.text}"}

如何混合使用

在 FastAPI 中可直接调用 Starlette 的功能:

from fastapi import FastAPI
from starlette.websockets import WebSocket

app = FastAPI()

# 直接使用 Starlette 的 WebSocket
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    await websocket.send_text("Connected via Starlette!")

生态系统

  • Starlette
    • 生态较简单,适合作为底层工具。
    • 常用扩展:starlette-admin(后台管理)、starlette-exporter(Prometheus 监控)。
  • FastAPI
    • 生态丰富,集成数据库(SQLAlchemy、TortoiseORM)、安全(OAuth2)、缓存等。
    • 热门插件:fastapi-users(用户管理)、fastapi-cache(缓存)。

Starlette深度解析

ASGI 应用入口 (app.py)

class Starlette:
    def __init__(self, routes=None, middleware=None, ...):
        self.router = Router(routes, ...)
        self.middleware_stack = self.build_middleware_stack(middleware)

    async def __call__(self, scope, receive, send):
        await self.middleware_stack(scope, receive, send)

    def build_middleware_stack(self, middleware):
        app = self.router
        for cls, options in reversed(middleware or []):
            app = cls(app=app, **options)
        return app
  • 核心机制:通过中间件包装路由,形成处理链
  • 执行流程:
    • 接收 ASGIscope, receive, send
    • 中间件按添加顺序逆序包裹(类似洋葱模型)
    • 最终由Router 处理请求

路由系统 (routing.py)

路由匹配逻辑

class Router:
    def __init__(self, routes):
        self.routes = []
        for route in routes:
            if isinstance(route, Route):
                self.add_route(route)
            elif isinstance(route, Mount):
                self.add_mount(route)

    async def handle(self, scope, receive, send):
        for route in self.routes:
            match, child_scope = route.matches(scope)
            if match == Match.FULL:
                scope.update(child_scope)
                await route.handle(scope, receive, send)
                return
  • 匹配优先级:按添加顺序优先匹配
  • 路径解析:使用parse 模块进行路径参数提取

路由类型

class Route:
    def __init__(self, path, endpoint, methods=None):
        self.path = path
        self.endpoint = endpoint  # 处理函数
        self.methods = methods

    async def handle(self, scope, receive, send):
        request = Request(scope, receive)
        response = await self.endpoint(request)
        await response(scope, receive, send)

请求/响应模型 (requests.py / responses.py)

请求封装

class Request:
    def __init__(self, scope, receive):
        self.scope = scope
        self._receive = receive
        self._stream_consumed = False

    async def stream(self):
        while True:
            event = await self._receive()
            if event["type"] == "http.request":
                yield event.get("body", b"")
            if event.get("more_body", False) is False:
                break

响应处理

class Response:
    def __init__(self, content, status_code=200, headers=None):
        self.body = self.render(content)
        self.status_code = status_code
        self.headers = Headers(headers or [])

    async def __call__(self, scope, receive, send):
        await send({
            "type": "http.response.start",
            "status": self.status_code,
            "headers": self.headers.raw,
        })
        await send({
            "type": "http.response.body",
            "body": self.body,
        })

中间件机制 (middleware.py)

class Middleware:
    def __init__(self, app, **options):
        self.app = app
        self.options = options

    async def __call__(self, scope, receive, send):
        async def modified_send(message):
            # 可修改响应消息
            await send(message)

        await self.app(scope, receive, modified_send)

典型中间件:

  • CORSMiddleware:跨域处理
  • GZipMiddleware:压缩响应
  • HTTPSRedirectMiddleware:HTTPS 重定向

WebSocket 支持 (websockets.py)

class WebSocket:
    async def accept(self, subprotocol=None):
        await self._send({"type": "websocket.accept", "subprotocol": subprotocol})

    async def receive(self):
        message = await self._receive()
        if message["type"] == "websocket.disconnect":
            raise WebSocketDisconnect(message["code"])
        return message

    async def send(self, data):
        if isinstance(data, str):
            await self._send({"type": "websocket.send", "text": data})
        else:
            await self._send({"type": "websocket.send", "bytes": data})

生命周期事件处理

class LifespanHandler:
    async def startup(self):
        for handler in self.on_startup:
            await handler()

    async def shutdown(self):
        for handler in self.on_shutdown:
            await handler()

    async def __call__(self, scope, receive, send):
        message = await receive()
        assert message["type"] == "lifespan.startup"
        await self.startup()
        await send({"type": "lifespan.startup.complete"})

        message = await receive()
        assert message["type"] == "lifespan.shutdown"
        await self.shutdown()
        await send({"type": "lifespan.shutdown.complete"})

性能优化设计

  • 惰性加载:请求对象仅在需要时创建
  • 零拷贝传输:大文件响应使用FileResponse 的流式传输
  • 高效头部处理:使用MultiDict 数据结构存储头部
  • 异步模板渲染:
class Jinja2Templates:
    async def TemplateResponse(self, name, context):
        template = self.get_template(name)
        content = template.render(context)
        return HTMLResponse(content)

核心设计模式

  • 组合优于继承:通过中间件堆叠扩展功能
  • 单一职责原则:每个类/函数专注单一功能
  • 协议抽象:统一处理 HTTP/WebSocket 等协议
  • 显式状态管理:严格区分客户端/服务器状态

通过这种模块化设计,Starlette 在保持代码精简(核心约 3,000 行)的同时,提供了高性能的 Web 开发基础。其设计哲学强调 “提供基础组件,而非完整解决方案”,这使得开发者可以灵活组合所需功能,而不被框架限制。

Starlette扩展能力

生态系统

工具 用途
uvicorn ASGI 服务器
httpx 异步 HTTP 客户端
databases 异步数据库连接池
python-multipart 表单数据处理
jinja2 模板引擎集成

数据库集成

from databases import Database

database = Database("postgresql://user:pass@localhost/db")

@app.on_event("startup")
async def connect_db():
    await database.connect()

@app.on_event("shutdown")
async def disconnect_db():
    await database.disconnect()

模板引擎

from starlette.templating import Jinja2Templates

templates = Jinja2Templates(directory="templates")

async def user_profile(request):
    return templates.TemplateResponse(
        "profile.html", {"request": request, "user": "Alice"}
    )

后台任务

async def send_notification(email: str):
    # 模拟发送邮件
    print(f"Sending email to {email}")

async def create_user(request):
    background = request.state.background
    background.add_task(send_notification, "user@example.com")
    return JSONResponse({"status": "created"})

参考链接:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注