第2节:构建后端核心API
这一节我们要让 FastAPI 变得“有用”起来。我们将不再返回简单的 "Hello World",而是返回真正的电影数据。
你需要做三件事:
-
创建电影数据:我们可以先编写假数据,后面再学习如何从数据库中获取真实数据。
-
随机电影接口:用于从电影数据中,随机筛选一个电影信息。
-
电影详情接口:用于显示这个电影的详细信息。
小知识:什么是接口
在开发中,“接口”(API)就像是一扇对外开放的窗口,它允许别人通过特定的地址(URL)来向你的服务提出请求,并获得相应的数据或结果。接口并不关心界面长什么样、视频怎么播放,它只负责“提供数据”和“执行操作”。
一句话理解: **接口就是前端、手机App、甚至其他程序与服务器沟通的桥梁。**它制定了规则:“你访问这个地址,我就给你返回什么。” 所以,当你能提供一个接口时,你的后端就真正开始“对外服务”了。
接下来,让我们一步一步来实现这些功能。
1.创建电影数据
在 main.py文件中,我们新建一个data_movie列表,包含几个示例的电影信息。代码如下:
from fastapi import FastAPI
app = FastAPI()
# --- 模拟数据库 ---
movies_data = [
{
"id": 1,
"title": "星际穿越 (Interstellar)",
"year": "2014",
"tags": "科幻 / 冒险 / 悬疑",
"score": 9.4,
"desc": "在地球环境日益恶化的未来,一组探险家利用新发现的虫洞,超越了人类太空旅行的极限,试图在广袤的宇宙中寻找人类的新家园。",
"bg": "https://images.unsplash.com/photo-1536440136628-849c177e76a1?q=80&w=2525&auto=format&fit=crop",
"director": "Christopher Nolan",
"actors": "Matthew McConaughey,Anne Hathaway,Jessica Chastain", # 注意:数据库存列表比较麻烦,这里先简化为字符串,中间用逗号隔开
"video_url": "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
},
{
"id": 2,
"title": "赛博朋克:边缘行者",
"year": "2022",
"tags": "科幻 / 动画 / 动作",
"score": 9.1,
"desc": "在一个沉迷于肉体改造的未来城市中,一名流浪街头的少年为了生存,选择成为一名雇佣兵——也就是众所周知的“赛博朋克”。",
"bg": "https://images.unsplash.com/photo-1626814026160-2237a95fc5a0?q=80&w=2070&auto=format&fit=crop",
"director": "Hiroyuki Imaishi",
"actors": "KENN,Aoi Yuki,Hiroki Touchi",
"video_url": "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4"
},
{
"id": 3,
"title": "爱乐之城 (La La Land)",
"year": "2016",
"tags": "爱情 / 歌舞 / 剧情",
"score": 8.5,
"desc": "米娅渴望成为一名演员,但至今她仍旧只是片场咖啡厅里的一名平凡巴师。塞巴斯汀醉心于爵士乐,但在现实面前只能在餐厅里弹奏解闷的乐曲。",
"bg": "https://images.unsplash.com/photo-1518609878373-06d740f60d8b?q=80&w=2070&auto=format&fit=crop",
"director": "Damien Chazelle",
"actors": "Ryan Gosling,Emma Stone,John Legend",
"video_url": "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4"
},
{
"id": 4,
"title": "沙丘 (Dune)",
"year": "2021",
"tags": "科幻 / 冒险 / 史诗",
"score": 8.8,
"desc": "天赋异禀的少年保罗·厄崔迪被命运指引,为了保卫自己的家族和人民,决心前往浩瀚宇宙间最危险的星球,开启一场惊心动魄的冒险。",
"bg": "https://images.unsplash.com/photo-1541963463532-d68292c34b19?q=80&w=2000&auto=format&fit=crop",
"director": "Denis Villeneuve",
"actors": "Timothée Chalamet,Rebecca Ferguson,Zendaya",
"video_url": "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4"
}
]
@app.get("/")
def read_root():
return {"message": "Hello Movie", "status": "OK"}
main.py中新增了一个movies_data列表,列表中的每一个元素就是一个字典,因为Python字典和JSON格式转化更为方便,形式如下:
[
{},
{},
]
说明:
movies_data只是为了演示方便,后面我们会用数据库来替换。我们想帮助更多的零基础入门小伙伴快速上手,所以采用了循序渐进的教学方式。这也是为什么大家喜欢大熊课堂的这种教学方式。
2.随机电影接口
我们创建随机电影接口,接口信息如下:
- URL:
/api/movie/random - 功能: 每次请求,都会随机返回一部电影的 JSON 数据。
- 原理: 使用 Python 的
random.choice()函数。
💻 代码实现
接下来,我们开始在main.py中继续新增代码:
from fastapi import FastAPI
import random
app = FastAPI()
# --- 模拟数据库 ---
movies_data = []
@app.get("/")
def read_root():
return {"message": "Hello Movie", "status": "OK"}
# 新增随机电影接口
@app.get("/api/movie/random")
async def get_random_movie():
return random.choice(movies_data)
为什么要用async
你在代码里看到了 async def,而不是普通的 def。
async 是 Asynchronous(异步)的缩写。
🍔 举个例子:麦当劳点餐
想象你在经营一家快餐店(这就是你的服务器),有两种工作模式:
-
传统模式 (同步
Sync):只有一个收银员。顾客A来了点个汉堡,收银员向厨房下单,然后站在柜台前傻等,直到厨房把汉堡做好,他递给顾客A,顾客A走了,他才接待顾客B。
- 缺点:如果厨房做汉堡慢,后面的顾客B、C、D全都在排队骂人。效率极低。
-
FastAPI 模式 (异步
Async):还是一个收银员。顾客A来了点个汉堡,收银员向厨房下单,给顾客A一个号牌,说:“您稍等”。他立刻转头接待顾客B。
- 优点:虽然厨房做汉堡的速度没变,但收银员(服务器)在等待的时间里可以处理其他人的请求。这就是 “高并发”。
# 定义一个异步函数
async def get_random_movie():
return ...
当你加上 async,就是在告诉 Python:“嘿,这个函数在处理任务时,如果遇到等待(比如读数据库、读文件),不要傻等,先去干别的事!”。这让 FastAPI 比传统的 Python 框架快很多。
@ 符号是什么?(装饰器)
@app.get("/api/movie/random")
def function(): ...
这个 @ 叫做 装饰器 (Decorator)。
你可以把它想象成便利贴或者路牌。
def function():这只是一个普普通通的 Python 函数,它躺在那里,没人知道它是干嘛的。@app.get(...):这是给 FastAPI 贴了个条子。条子上写着:“如果有用户通过浏览器(GET方式)访问/api/movie/random这个地址,请立马叫醒下面这个函数来干活!”
如果没有这个 @,你的函数就是一个“孤岛”,外界永远访问不到它。
✅ 测试接口
保存代码后,终端应该会自动重载。如果没有,请手动运行 uvicorn main:app --reload。
打开浏览器,访问网址:http://127.0.0.1:8000/api/movie/random,运行效果如下图所示。

说明:
因为是随机生成的,所以大家显示的结果可能与我不同。每次刷新页面,也会随机生成一个新的。
打开浏览器访问交互文档:http://127.0.0.1:8000/docs
- 找到 GET /api/movie/random 栏目,点开。
- 点击 Try it out -> Execute。
- 观察 Response body。
- 再次点击 Execute。观察返回的电影是不是变了?如果是,说明随机功能正常!
运行效果如下图所示。

3.电影详情接口
电影详情接口信息如下:
- URL:
/api/movie/{movie_id} - 功能: 根据 URL 中传入的 ID,返回特定电影。
- 类型检查: 注意代码中的
movie_id: int。如果你访问/api/movie/abc,FastAPI 会自动报错,因为它知道abc不是整数。这就是 FastAPI 的强大之处!
💻 代码实现
在main.py函数中,创建get_movie_by_id()函数。代码如下:
from fastapi import FastAPI,HTTPException
# --- 3. 电影详情接口 (带参数) ---
# 定义路由:当网址是 /api/movie/1 时,1 会被赋值给 movie_id
@app.get("/api/movie/{movie_id}")
async def get_movie_by_id(movie_id: int):
# 1. 遍历:去 movies 列表里一个一个翻
for movie in movies_data:
# 2. 比对:如果这部电影的 id 等于用户传进来的 id
if movie["id"] == movie_id:
# 3. 找到啦!直接把这一整条数据返回去
return movie
# 4. 兜底:如果循环都跑完了(所有电影都看过了),还没 return,说明没找到。
# 这时候不能装作无事发生,必须“报错”。
# 404 是 HTTP 协议中代表“未找到”的标准代号。
raise HTTPException(status_code=404, detail="未找到该 ID 的电影")
为什么写 movie_id: int?(类型提示)
async def get_movie_by_id(movie_id: int):
这里的 : int 叫做 类型提示 (Type Hint)。
在以前的 Python 里,大家不怎么写这个。但在 FastAPI 里,它充当了 “安检员” 的角色。
- 场景 A (没有安检):用户访问
/api/movie/abc。函数拿到了字符串"abc",然后去数据库里找 ID 为"abc"的电影。程序可能会崩溃,或者报错报得很奇怪。 - 场景 B (有
: int安检):用户访问/api/movie/abc。FastAPI 的安检员一看:“咦?这里的规则要求是整数 (int),你给我的是字母?”- FastAPI 会直接拦截,拒绝执行函数,并自动返回一个清晰的错误信息给用户:“value is not a valid integer”。
这帮你省去了大量的 if type(movie_id) != int: 这种繁琐的检查代码。
为什么要使用HTTPException
用户请求 999,但系统只有 1–4,那你必须告诉用户结果:
✔ 找不到 ✔ 并用正确的 HTTP 状态码返回 ✔ 而不是假装没事
这是一个合格 API 的责任
return 的字典去哪了?(JSON 序列化)
return {"id": 1, "title": "..."}
我们在 Python 里写的是 字典 (Dictionary),但在浏览器里看到的是 JSON。
- Python 字典:只有 Python 认识的数据格式。
- JSON:互联网通用的“普通话”。前端(JS)、后端(Python/Java/Go)、手机App都能读懂。
FastAPI 默默地做了一个巨大的贡献:它自动把你的 Python 字典“翻译”成了 JSON 字符串,然后发给浏览器。你完全不需要手动去调用 json.dumps()。
测试接口
保存代码后,终端应该会自动重载。如果没有,请手动运行 uvicorn main:app --reload。
✅ 测试任务 :获取指定电影
打开浏览器,访问网址:http://127.0.0.1:8000/api/movie/1,运行效果如下图所示。

✅ 测试任务 :测试错误的类型
打开浏览器,访问网址:http://127.0.0.1:8000/api/movie/aaa。因为`aaa`不是指定的Int类型,运行效果如下:

✅ 测试任务 :测试错误处理 (404)
如果访问一个不存在的id数字,例如,访问网址:http://127.0.0.1:8000/api/movie/999。运行效果如下:

4.总结
到目前为止,你已经理解:
✔ 如何创建模拟数据库
✔ 如何返回结构化 JSON
✔ 如何构建 REST API
✔ 什么是 async
✔ 什么是 类型检查
✔ 如何做错误处理
✔ 如何测试 API
目前为止我们还没有看到网站的页面,别急,下一节我们就来引入HTML页面,让你能在项目中看到效果!
【大熊课堂精品课程】
Python零基础入门动画课: https://www.bilibili.com/cheese/play/ss7988
Django+Vue:全栈开发: https://www.bilibili.com/cheese/play/ss8134
PyQT6开发桌面软件: https://www.bilibili.com/cheese/play/ss12314
Python办公自动化: https://www.bilibili.com/cheese/play/ss14990
Cursor AI编程+MCP:零基础实战项目课: https://www.bilibili.com/cheese/play/ss105194189
Pandas数据分析实战: https://www.bilibili.com/cheese/play/ss734522035
AI大模型+Python小白应用实战: https://www.bilibili.com/cheese/play/ss3844