Skip to content

FastAPI 基础

前端用 fetch 调的接口,背后是一个正在运行的服务进程——它监听某个端口,接收 HTTP 请求,处理后返回 JSON。这个服务进程可以用很多方式写,FastAPI 是 Python 生态里写这类接口最主流的框架之一。

一、为什么用框架

不借助任何框架,用 Python 标准库也能写一个 HTTP 服务:

python
from http.server import HTTPServer, BaseHTTPRequestHandler


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        self.wfile.write(b'{"message": "hello"}')


HTTPServer(("127.0.0.1", 8000), Handler).serve_forever()

能跑,但问题马上来:一个 URL 对应一个 if self.path == "/articles" 分支,JSON 序列化手动写,请求体手动解析,跨域手动处理,参数校验手动判断……接口一多,代码全是这些重复活。

框架就是把这些重复活收走了——URL 和函数的对应关系用装饰器声明,JSON 序列化自动做,参数校验声明式配置,接口文档自动生成。FastAPI 在这些基础上还带了类型提示和异步支持,写起来跟写普通 Python 函数差不多。

二、安装和第一个接口

bash
uv init article-api
cd article-api
uv add fastapi uvicorn

fastapi 是框架本身,uvicorn 是 ASGI 服务器——负责接收 HTTP 连接、转给 FastAPI 处理。两个都要装。

新增文件 main.py

python
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def root():
    return {"message": "hello"}

@app.get("/") 是装饰器(Python 进阶语法篇介绍过 @ 语法)——告诉 FastAPI:"GET 请求 / 这个路径时,执行下面的函数"。函数返回字典,FastAPI 自动转成 JSON 响应。

启动开发服务器:

bash
uv run uvicorn main:app --reload --port 8000

main:app 表示 main.py 文件里的 app 变量,--reload 改代码自动重启。打开浏览器访问 http://localhost:8000/,看到 {"message": "hello"} 就成了。

FastAPI 还自带一个交互式文档——访问 http://localhost:8000/docs,能看到所有接口列表,还能直接在页面上测试。这个文档是自动生成的,不需要手写。

三、路径参数

URL 里的变量部分用花括号标记:

python
@app.get("/articles/{article_id}")
def get_article(article_id: int):
    return {"article_id": article_id, "title": "Python 入门"}

{article_id} 从 URL 取值,传给函数参数。类型标注 int 不是摆设——FastAPI 会自动校验:访问 /articles/abc 会返回 422 错误,提示 article_id 必须是整数。

访问 http://localhost:8000/articles/1

json
{"article_id": 1, "title": "Python 入门"}

这就是前端 fetch("/articles/1") 拿到的响应——前端的 fetch 和后端的接口,就是这样对应的。前端拼 URL,后端用路由匹配 URL,两边约定好格式就能通信。

四、查询参数

URL 问号后面的参数,函数参数直接写就行:

python
@app.get("/articles")
def list_articles(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

访问 /articles?skip=0&limit=5skiplimit 自动从 URL 解析。带默认值的参数是可选的,不传就用默认值。

路径参数和查询参数的区别:路径参数是 URL 路径的一部分(/articles/{id}),查询参数是问号后面的(?skip=0)。FastAPI 根据参数名是否在路径里出现,自动区分两者。

五、模拟文章数据

完整连数据库的例子放在数据库篇。这里先用内存列表模拟数据,把接口跑通:

python
from fastapi import FastAPI

app = FastAPI()

# 内存里的文章列表,重启就没了——后面会换成数据库
articles = [
    {"id": 1, "title": "Python 入门", "status": "published"},
    {"id": 2, "title": "Go 并发", "status": "draft"},
    {"id": 3, "title": "Docker 部署", "status": "published"},
]


@app.get("/articles")
def list_articles(status: str = None):
    """列出文章,可选按状态过滤。"""
    if status:
        return [a for a in articles if a["status"] == status]
    return articles


@app.get("/articles/{article_id}")
def get_article(article_id: int):
    """获取单篇文章。"""
    for a in articles:
        if a["id"] == article_id:
            return a
    return {"error": "not found"}

启动后测试:

bash
# 列出全部
curl http://localhost:8000/articles

# 只看已发布的
curl "http://localhost:8000/articles?status=published"

# 获取单篇
curl http://localhost:8000/articles/1

六、前端怎么调这些接口

前端 06 篇封装的 request 函数,URL 是 /articles。如果前端和后端部署在不同端口,需要处理跨域。开发阶段可以先加个 CORS 中间件让前端能调通:

python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# 开发阶段允许前端跨域访问——生产环境要收紧允许的来源
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5173"],  # 前端开发服务器地址
    allow_methods=["*"],
    allow_headers=["*"],
)

加上这段,前端跑在 localhost:5173 就能调 localhost:8000 的接口了。CORS 的原理在 DOM 与请求篇提到过——浏览器拦的不是网络请求本身,而是跨域的响应。后端通过 CORS 中间件告诉浏览器"我允许这个来源",浏览器就放行了。

这段跨域配置的完整说明放在后端进阶的 CORS 篇,这里先加上让前后端跑通。

七、开发服务器和热重载

uvicorn main:app --reload 是开发时用的。--reload 监听文件变化自动重启,改完代码保存就能看到效果,不用手动停掉重启。

生产环境不用 --reload(开销大且不稳定),换成多 worker 模式:

bash
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

--workers 4 开 4 个进程处理请求,提升并发能力。--host 0.0.0.0 允许外部访问(开发时默认只绑 127.0.0.1)。部署相关的细节放在项目实战的部署篇。