2.3.3 项目:Web API 开发

这个项目把 Python 从脚本带到服务端。你会用 FastAPI 把功能包装成可被其他程序调用的接口,理解 API 如何连接模型、应用和用户,为后面的 AI 应用开发打基础。
- 理解什么是 API,为什么 AI 工程师需要会写 API
- 学会使用 FastAPI 框架构建 Web API
- 掌握 RESTful API 的基本设计原则
- 构建一个可以被其他程序调用的 AI 服务接口
为什么 AI 工程师需要会写 API?
Section titled “为什么 AI 工程师需要会写 API?”你训练了一个很棒的 AI 模型——然后呢?
模型训练好只是第一步。要让别人使用你的模型,你需要把它包装成一个 API 服务:
你的 AI 模型 → 包装成 API → 手机 App / 网站 / 其他程序 调用
具体例子:- ChatGPT 模型 → 通过 API 提供服务 → 各种 App 调用- 图像识别模型 → 通过 API → 用户上传图片获得识别结果- 推荐算法 → 通过 API → 电商网站展示推荐商品所以 API 是连接 AI 模型和真实世界的桥梁。
什么是 API?
Section titled “什么是 API?”API(Application Programming Interface) = 应用程序编程接口。
简单理解:API 就是一个程序和程序之间的”对话窗口”。
你去餐厅吃饭的过程就是一个 API 调用:
你(客户端) → 向服务员(API)说"一碗牛肉面"(请求)服务员 → 传给后厨(服务器)后厨 → 做好面服务员 → 把面端给你(响应)你不需要知道后厨怎么做的面,你只需要知道怎么点餐(发请求)和怎么接面(收响应)。
Web API 的核心概念
Section titled “Web API 的核心概念”| 概念 | 说明 | 类比 |
|---|---|---|
| URL(端点) | API 的地址 | 餐厅地址 |
| HTTP 方法 | 操作类型 | 点餐 / 退菜 / 加菜 |
| 请求体 | 发送的数据 | 你要的菜名 |
| 响应 | 返回的结果 | 端上来的菜 |
| 状态码 | 操作是否成功 | 200=成功, 404=没这道菜 |
HTTP 方法
Section titled “HTTP 方法”| 方法 | 用途 | 示例 |
|---|---|---|
GET | 获取数据 | 获取任务列表 |
POST | 创建数据 | 添加新任务 |
PUT | 更新数据(整体) | 修改任务全部信息 |
DELETE | 删除数据 | 删除一个任务 |
第一步:安装 FastAPI
Section titled “第一步:安装 FastAPI”pip install fastapi uvicorn| 库 | 作用 |
|---|---|
fastapi | Web 框架,用来编写 API |
uvicorn | ASGI 服务器,用来运行 FastAPI 应用 |
第二步:Hello World API
Section titled “第二步:Hello World API”创建文件 main.py:
from fastapi import FastAPI
# 创建应用实例app = FastAPI(title="我的第一个 API", version="1.0")
# 定义一个端点@app.get("/")def root(): return {"message": "Hello, World!", "status": "running"}
@app.get("/hello/{name}")def hello(name: str): return {"message": f"你好,{name}!", "name": name}启动服务器:
uvicorn main:app --reloadmain= 文件名(main.py)app= FastAPI 实例名--reload= 代码修改后自动重启(开发时用)
打开浏览器访问:
http://127.0.0.1:8000→ 看到 Hello Worldhttp://127.0.0.1:8000/hello/小明→ 看到个性化问候http://127.0.0.1:8000/docs→ 自动生成的交互式 API 文档!
第三步:构建任务管理 API
Section titled “第三步:构建任务管理 API”让我们把之前的命令行任务管理器改造成 Web API:
"""任务管理 API运行: uvicorn main:app --reload文档: http://127.0.0.1:8000/docs"""
from fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModelfrom datetime import datetime
# 创建应用app = FastAPI( title="任务管理 API", description="一个简单的 RESTful 任务管理接口", version="1.0")
# ---------- 数据模型 ----------
class TaskCreate(BaseModel): """创建任务时的请求体""" title: str priority: str = "中"
class TaskUpdate(BaseModel): """更新任务时的请求体""" title: str | None = None priority: str | None = None done: bool | None = None
class Task(BaseModel): """任务的完整数据""" id: int title: str priority: str done: bool created_at: str
# ---------- 模拟数据库(内存存储) ----------
tasks_db: list[dict] = []next_id: int = 1
# ---------- API 端点 ----------
@app.get("/")def root(): """API 首页""" return { "name": "任务管理 API", "version": "1.0", "endpoints": { "查看所有任务": "GET /tasks", "创建任务": "POST /tasks", "查看单个任务": "GET /tasks/{task_id}", "更新任务": "PUT /tasks/{task_id}", "删除任务": "DELETE /tasks/{task_id}", "API 文档": "GET /docs" } }
@app.get("/tasks")def get_tasks(done: bool | None = None): """ 获取所有任务。
可选参数: - done: 过滤已完成(true)或未完成(false)的任务 """ if done is not None: filtered = [t for t in tasks_db if t["done"] == done] return {"count": len(filtered), "tasks": filtered} return {"count": len(tasks_db), "tasks": tasks_db}
@app.post("/tasks", status_code=201)def create_task(task: TaskCreate): """创建新任务""" global next_id
new_task = { "id": next_id, "title": task.title, "priority": task.priority, "done": False, "created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S") } tasks_db.append(new_task) next_id += 1
return {"message": "任务创建成功", "task": new_task}
@app.get("/tasks/{task_id}")def get_task(task_id: int): """获取指定 ID 的任务""" for task in tasks_db: if task["id"] == task_id: return task
# 找不到任务,返回 404 raise HTTPException(status_code=404, detail=f"任务 {task_id} 不存在")
@app.put("/tasks/{task_id}")def update_task(task_id: int, task_update: TaskUpdate): """更新任务""" for task in tasks_db: if task["id"] == task_id: if task_update.title is not None: task["title"] = task_update.title if task_update.priority is not None: task["priority"] = task_update.priority if task_update.done is not None: task["done"] = task_update.done return {"message": "更新成功", "task": task}
raise HTTPException(status_code=404, detail=f"任务 {task_id} 不存在")
@app.delete("/tasks/{task_id}")def delete_task(task_id: int): """删除任务""" for i, task in enumerate(tasks_db): if task["id"] == task_id: removed = tasks_db.pop(i) return {"message": "删除成功", "task": removed}
raise HTTPException(status_code=404, detail=f"任务 {task_id} 不存在")
@app.get("/stats")def get_stats(): """获取任务统计信息""" total = len(tasks_db) done = sum(1 for t in tasks_db if t["done"]) return { "total": total, "done": done, "pending": total - done, "completion_rate": f"{done/total:.1%}" if total > 0 else "0%" }# 启动服务器uvicorn main:app --reload然后打开 http://127.0.0.1:8000/docs,你可以在浏览器中直接测试所有 API。
也可以用命令行测试:
# 创建任务curl -X POST http://127.0.0.1:8000/tasks \ -H "Content-Type: application/json" \ -d '{"title": "学习 FastAPI", "priority": "高"}'
# 查看所有任务curl http://127.0.0.1:8000/tasks
# 完成任务curl -X PUT http://127.0.0.1:8000/tasks/1 \ -H "Content-Type: application/json" \ -d '{"done": true}'
# 删除任务curl -X DELETE http://127.0.0.1:8000/tasks/1或者用 Python 的 requests 库:
import requests
BASE_URL = "http://127.0.0.1:8000"
# 创建任务resp = requests.post(f"{BASE_URL}/tasks", json={"title": "学习 Python", "priority": "高"})print(resp.json())
# 获取所有任务resp = requests.get(f"{BASE_URL}/tasks")print(resp.json())理解 Pydantic 数据模型
Section titled “理解 Pydantic 数据模型”FastAPI 使用 Pydantic 来验证请求数据——你定义好数据模型,FastAPI 自动帮你检查:
from pydantic import BaseModel, Field
class TaskCreate(BaseModel): title: str = Field(..., min_length=1, max_length=100, description="任务标题") priority: str = Field(default="中", description="优先级: 高/中/低")
# 如果用户发送了不合法的数据# POST /tasks {"title": ""} → 自动返回 422 错误(标题太短)# POST /tasks {} → 自动返回 422 错误(缺少 title)# POST /tasks {"title": "OK"} → 成功,priority 使用默认值 "中"你不需要手动写验证代码——Pydantic 和 FastAPI 帮你搞定了。
挑战 1:添加文件持久化
Section titled “挑战 1:添加文件持久化”现在数据存在内存里,服务器重启就没了。改成用 JSON 文件保存。
挑战 2:添加搜索功能
Section titled “挑战 2:添加搜索功能”@app.get("/tasks/search")def search_tasks(keyword: str): """按关键词搜索任务""" keyword_lower = keyword.lower() return [task for task in tasks if keyword_lower in task.title.lower()]挑战 3:添加分页
Section titled “挑战 3:添加分页”当任务很多时,支持分页返回:
GET /tasks?page=1&size=10挑战 4:接入 AI 模型
Section titled “挑战 4:接入 AI 模型”提前预习:创建一个 /predict 端点,接收文本输入,返回情感分析结果。
项目交付参考与讲解
- 启动时加载任务,增删改后保存,这样持久化更稳定。让存储的 JSON 结构尽量接近响应结构,便于调试。
- 增加
/tasks/search,对标题做不区分大小写的子串匹配。 - 通过
page和size实现分页,在返回前切片任务列表。 - 把 AI 端点先当作扩展功能,前提是 CRUD API 已经稳定。复用同一套校验和错误处理风格,这样新路由行为更可预测。
- 自查:打开
/docs,用curl测每个端点,并确认无效请求体会触发 FastAPI 自动生成的 422 校验错误。
项目自查清单
Section titled “项目自查清单”- API 能正常启动和访问
- 实现了完整的 CRUD(增删改查)操作
- 使用了 Pydantic 做数据验证
- 有适当的错误处理(HTTPException)
- 自动生成的 API 文档(
/docs)可以正常使用 - 能用 curl 或 requests 测试所有端点
版本路线建议
Section titled “版本路线建议”| 版本 | 目标 | 交付重点 |
|---|---|---|
| 基础版 | 跑通最小闭环 | 能输入、能处理、能输出,并保留一组示例 |
| 标准版 | 形成可展示项目 | 增加配置、日志、错误处理、README 和截图 |
| 挑战版 | 接近作品集质量 | 增加评估、对比实验、失败样本分析和下一步路线 |
建议先完成基础版,不要一开始就追求大而全。每提升一个版本,都要把“新增了什么能力、怎么验证、还有什么问题”写进 README。
学完这一页,至少保留这张证据卡:
- 项目目标
- CLI、爬虫、API、AI API 调用,或集成式 Python 工作坊目标
- 运行命令
- 启动项目时使用的准确命令
- 工件
- 输出文件、API 响应、JSON 记录、截图或 README 说明
- 失败检查
- 依赖、网络、解析、路由、输入验证或 API key 问题
- 期望产出
- 可复现的迷你项目文件夹,包含运行结果和一个失败案例