1. 什么是 Starlette? #

Starlette 是一个用来写 Web 接口的 Python 框架。你可以用它定义路由(如 /hello)、处理 HTTP 请求、返回 JSON 或 HTML 等响应。它轻量、支持异步,是 FastAPI 的底层框架。

为什么学 Starlette?

2. 前置知识 #

Web 框架与路由

ASGI 是什么?

异步(async/await)

3. 安装 #

在命令行中执行:

pip install starlette uvicorn

如需解析表单数据(如 application/x-www-form-urlencoded),可额外安装:

pip install python-multipart

4. 第一个应用 #

下面是一个完整的 Starlette 应用,定义了一个根路径 /,返回 JSON。

步骤 1:新建 app.py,写入:

# 导入 Starlette 应用类、路由、JSON 响应
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route


# 处理根路径 / 的请求,接收 request 参数
async def homepage(request):
    # 返回 JSON 响应,自动设置 Content-Type 为 application/json
    return JSONResponse({"message": "Hello, Starlette!"})


# 定义路由列表:路径 / 对应 homepage 函数
routes = [
    Route("/", endpoint=homepage),
]

# 创建 Starlette 应用,传入路由
app = Starlette(routes=routes)

步骤 2:在终端执行:

uvicorn app:app

步骤 3:浏览器访问 http://127.0.0.1:8000,会看到 {"message":"Hello, Starlette!"}。

5. 用装饰器定义路由 #

除了用 Route 列表,也可以用装饰器 `@app.route()` 添加路由,写法更简洁。

# 导入 Starlette 和 JSONResponse
from starlette.applications import Starlette
from starlette.responses import JSONResponse

# 先创建应用(此时没有路由)
app = Starlette()

# 用装饰器把路径 / 绑定到 homepage 函数
@app.route("/")
async def homepage(request):
    return JSONResponse({"message": "Hello, World!"})

注意:装饰器方式需要先 app = Starlette(),再写 `@app.route()`。两种方式可以混用。

6. 请求对象(request)常用属性 #

路由处理函数会收到一个 request 参数,通过它可以获取请求信息。

属性/方法 说明
request.method HTTP 方法,如 GET、POST
request.url 请求的完整 URL
request.headers 请求头(类似字典)
request.query_params 查询参数,如 ?name=张三
request.path_params 路径参数,如 /user/123 中的 123
await request.json() 解析 JSON 请求体
await request.body() 原始请求体(bytes)

7. 路径参数 #

路径参数是 URL 中的变量,例如 /user/张三 中的 张三。用 {name} 定义,在 request.path_params 中获取。

# 导入 Starlette 和 JSONResponse
from starlette.applications import Starlette
from starlette.responses import JSONResponse

app = Starlette()


# 根路径
@app.route("/")
async def index(request):
    return JSONResponse({"message": "欢迎"})


# 路径 /hello/xxx,xxx 会存入 path_params["name"]
@app.route("/hello/{name}")
async def greet(request):
    # 从路径参数中取出 name
    name = request.path_params["name"]
    return JSONResponse({"greeting": f"你好, {name}!"})


# 路径 /user/{user_id}/post/{post_id},多个路径参数
@app.route("/user/{user_id}/post/{post_id}")
async def user_post(request):
    user_id = request.path_params["user_id"]
    post_id = request.path_params["post_id"]
    return JSONResponse({"user_id": user_id, "post_id": post_id})

运行:uvicorn app:app,然后访问 http://127.0.0.1:8000/hello/张三。

8. 查询参数与请求体 #

查询参数:URL 中 ? 后面的部分,如 ?page=1&size=10。

请求体:POST 等请求携带的数据,常见格式为 JSON。

# 导入 Starlette 和 JSONResponse
from starlette.applications import Starlette
from starlette.responses import JSONResponse

app = Starlette()


# 获取查询参数,例如 /search?q=python&page=1
@app.route("/search")
async def search(request):
    # query_params 类似字典,支持 .get() 避免 KeyError
    q = request.query_params.get("q", "")
    page = request.query_params.get("page", "1")
    return JSONResponse({"q": q, "page": page})


# 接收 JSON 请求体,例如 POST /api/user,body: {"name":"小明","age":18}
@app.route("/api/user", methods=["POST"])
async def create_user(request):
    # 解析 JSON 请求体为字典
    data = await request.json()
    name = data.get("name", "")
    age = data.get("age", 0)
    return JSONResponse({"created": True, "name": name, "age": age})

注意:methods=["POST"] 表示只接受 POST 请求,不写则默认接受所有方法。

9. 常用响应类型 #

Starlette 提供多种响应类,常用如下:

类 说明
JSONResponse 返回 JSON
HTMLResponse 返回 HTML
PlainTextResponse 返回纯文本
Response 通用响应,可自定义状态码、头部
# 导入多种响应类型
from starlette.applications import Starlette
from starlette.responses import JSONResponse, HTMLResponse, PlainTextResponse, Response

app = Starlette()


# 返回 JSON
@app.route("/json")
async def json_view(request):
    return JSONResponse({"key": "value"})


# 返回 HTML
@app.route("/html")
async def html_view(request):
    return HTMLResponse("<h1>Hello</h1><p>这是 HTML</p>")


# 返回纯文本
@app.route("/text")
async def text_view(request):
    return PlainTextResponse("纯文本内容")


# 自定义状态码和响应头
@app.route("/custom")
async def custom_view(request):
    return Response(
        content="自定义响应",
        status_code=201,
        headers={"X-Custom": "my-header"},
    )

10. 添加 CORS 中间件 #

当前端(如浏览器中的 JavaScript)请求你的 API 时,浏览器会做「跨域」检查。若接口未设置 CORS,可能被浏览器拦截。通过 CORS 中间件可以允许指定来源的跨域请求。

# 导入 Starlette、路由、响应、中间件
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware


async def api_handler(request):
    return JSONResponse({"data": "ok"})


# 路由列表
routes = [
    Route("/api", endpoint=api_handler),
]

# 中间件列表:允许所有来源(开发时常用,生产环境建议指定具体域名)
middleware = [
    Middleware(CORSMiddleware, allow_origins=["*"]),
]

# 创建应用时传入 middleware,注意顺序:后添加的先执行
app = Starlette(routes=routes, middleware=middleware)

11. 可独立运行的项目 #

下面是一个包含多个路由、路径参数、查询参数、JSON 请求体和 CORS 的项目。

文件:demo_app.py

# 导入 Starlette 相关组件
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware


# 根路径
async def index(request):
    return JSONResponse({"message": "Starlette 入门示例", "docs": "/hello/{name}, /search?q=xxx, POST /api/echo"})


# 路径参数示例
async def greet(request):
    name = request.path_params.get("name", "访客")
    return JSONResponse({"greeting": f"你好, {name}!"})


# 查询参数示例
async def search(request):
    q = request.query_params.get("q", "")
    return JSONResponse({"query": q, "results": []})


# POST JSON 示例
async def echo(request):
    data = await request.json()
    return JSONResponse({"echo": data})


# 路由定义
routes = [
    Route("/", endpoint=index),
    Route("/hello/{name}", endpoint=greet),
    Route("/search", endpoint=search),
    Route("/api/echo", endpoint=echo, methods=["POST"]),
]

# CORS 中间件
middleware = [
    Middleware(CORSMiddleware, allow_origins=["*"]),
]

# 创建应用
app = Starlette(routes=routes, middleware=middleware)

运行:uvicorn demo_app:app --reload

测试:

12. 与 FastAPI 的关系 #

FastAPI 基于 Starlette 构建,在 Starlette 之上增加了:

如何选择?

13. 常见问题 #

13.1 如何只允许 POST 请求? #

在 Route 中指定 methods:

# 导入路由和响应
from starlette.routing import Route
from starlette.responses import JSONResponse

# 只接受 POST
Route("/submit", endpoint=submit_handler, methods=["POST"])

13.2 如何返回 404 或 500? #

使用 HTTPException 或自定义 Response 的状态码:

# 导入 HTTPException
from starlette.exceptions import HTTPException

@app.route("/notfound")
async def notfound(request):
    # 抛出 404,Starlette 会转换为响应
    raise HTTPException(status_code=404, detail="未找到")

13.3 如何获取请求头? #

通过 request.headers,类似字典:

# 获取 User-Agent
user_agent = request.headers.get("user-agent", "")

# 获取 Authorization
auth = request.headers.get("authorization", "")

14. 总结 #