下载工作台
Flask Web 开发

毕业项目:api-demo 服务

试读上半部分 · 解锁后可读全文

第 12 章 · 毕业项目:api-demo 服务 MVP

本章目标:在 ch01~ch11 基础上,独立交付 api-demo Flask API 服务最小可行产品(MVP);覆盖用户认证商品 API简易管理页运维文档;按 5 天计划实施;通过 100 分验收表自评;完成答辩演示与项目说明。

学时建议:5 天 × 6~8 小时(合计 30~40 小时)

前置:完成本模块 ch01~ch11;建议复习 python-dev ch06 OOP 与 ch11 requests;前端联调可参考 frontend-framework API 章节。


12.1 项目背景与边界

api-demo 是一家虚构的「轻量商品 API 平台」,面向运营人员提供后台管理能力,同时为 SPA / 移动端提供 JSON 接口。C 端前端部署在独立域名,调用 https://api.example.com(或你本地部署的等价地址)。

┌──────────────────────────────────────────────────┐
│              api-demo 服务 MVP(本章)             │
├──────────┬──────────┬──────────┬─────────────────┤
│ 用户认证  │ 商品 API  │ 管理页面  │  运维与测试     │
│ 注册登录  │ CRUD+分页 │ 列表表单  │  pytest/Gunicorn│
└──────────┴──────────┴──────────┴─────────────────┘
                        │
                        ▼
              PostgreSQL / SQLite(教学可用)
              Redis(缓存,ch10)+ Celery(选修)
模块MVP 必须不做(加分扩展)
用户注册、登录、退出、/api/v1/meOAuth、短信验证码
商品列表、搜索、创建/编辑、封面上传、上下架SKU 矩阵、批量导入
管理页登录后商品列表 + 表单 CRUD复杂权限 RBAC
APIRESTful + 统一错误码 + 分页GraphQL、gRPC
运维.env.example、Gunicorn 文档、日志K8s 全量(见云原生课)
严禁将真实公司内部域名、数据库连接串、密钥写入仓库或答辩材料。统一使用 api-demouser-demoapi.example.com

12.2 技术栈清单

层级技术对应章节
框架Flask 3.x + 应用工厂ch01
路由Blueprint(web + api)ch02
模板Jinja2 管理页ch03
表单WTForms + CSRFch04
ORMFlask-SQLAlchemych05
认证Flask-Login(见 12.4 方案对比)ch06
APIjsonify + 统一 envelopech07
上传UPLOAD_FOLDER + 分环境 Configch08
横切请求 ID、日志、errorhandlerch09
性能Redis 缓存热门列表(选修)ch10
异步Celery 注册通知(选修)ch10
测试pytest + test clientch11
部署Gunicorn + Nginx + .envch11
cd ~/python-learn
mkdir api-demo && cd api-demo
python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install "Flask>=3.0" Flask-SQLAlchemy Flask-Login Flask-WTF Flask-Caching \
            flask-cors python-dotenv Pillow celery redis \
            pytest pytest-cov gunicorn psycopg2-binary

12.3 推荐目录结构

api-demo/
├── .env.example
├── .gitignore
├── docker-compose.dev.yml       # Redis
├── gunicorn.conf.py
├── wsgi.py
├── pytest.ini
├── requirements.txt
├── docs/
│   ├── API.md                   # 接口文档
│   ├── DEPLOY.md                # 部署说明
│   └── SELF_REVIEW.md           # 100 分自评表
├── api_demo/
│   ├── __init__.py              # create_app
│   ├── config.py
│   ├── extensions.py
│   ├── celery_app.py
│   ├── logging_config.py
│   ├── api/
│   │   ├── __init__.py
│   │   ├── products.py
│   │   ├── auth.py
│   │   └── errors.py
│   ├── web/
│   │   ├── __init__.py
│   │   ├── views.py
│   │   └── forms.py
│   ├── models/
│   │   ├── user.py
│   │   └── product.py
│   ├── middleware/
│   └── tasks/
├── templates/
├── uploads/
└── tests/
    ├── conftest.py
    └── test_api_products.py

12.4 认证方案:Session 与 JWT 二选一

毕业项目必须实现一种完整认证链路;答辩时说明选型理由。

方案 A:Session + Cookie(推荐入门)

优点缺点
与 ch06 Flask-Login 一致跨域需 supports_credentials
管理页与 API 共享登录态移动端原生需 Cookie 管理
CSRF 由 Flask-WTF 保护 Web 表单水平扩展需 Redis Session(选修)

API 写操作:@login_required + unauthorized_handler 返回 JSON 401。

方案 B:JWT Bearer Token

pip install PyJWT
优点缺点
无状态,适合 SPA/移动端注销需黑名单或短过期
跨域简单,Header 传 Token需自行实现刷新 Token
与前后端完全分离XSS 泄露 Token 风险更高

登录返回

{
  "code": 0,
  "data": {
    "access_token": "eyJ...",
    "expires_in": 3600,
    "token_type": "Bearer"
  }
}

请求头Authorization: Bearer eyJ...

验收SessionJWT
登录后调 /api/v1/me 成功Cookie 自动携带Header 带 Token
未登录写操作 401
管理页登录可仅 API 登录

答辩话术:「MVP 选用 Session 降低复杂度;若 C 端为纯 SPA 且多端复用,生产可迁移 JWT。」


12.5 API 契约(最小集)

基址:https://api.example.com/api/v1(本地 http://127.0.0.1:5000/api/v1

方法路径认证说明
POST/auth/register注册
POST/auth/login登录
POST/auth/logout退出
GET/me当前用户
GET/products列表 + page per_page q
GET/products/{id}详情
POST/products创建
PATCH/products/{id}更新
DELETE/products/{id}删除
POST/products/{id}/cover封面上传

统一响应:{ "code": 0, "message": "ok", "data": ... };错误见 ch07 错误码表。

12.5.1 curl 冒烟脚本(答辩备用)

将下列脚本保存为 scripts/smoke_api.sh,答辩前一键验证核心链路:

BASE="http://127.0.0.1:5000/api/v1"
COOKIE_JAR="/tmp/api-demo-cookie.txt"

# 1. 列表(匿名)
curl -s "$BASE/products?page=1&per_page=5" | python -m json.tool

# 2. 注册 + 登录(Session)
curl -s -c "$COOKIE_JAR" -X POST "$BASE/auth/register" \
  -H "Content-Type: application/json" \
  -d '{"email":"demo@user-demo.example.com","password":"DemoPass123!"}'
curl -s -b "$COOKIE_JAR" -c "$COOKIE_JAR" -X POST "$BASE/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email":"demo@user-demo.example.com","password":"DemoPass123!"}'

# 3. 当前用户
curl -s -b "$COOKIE_JAR" "$BASE/me" | python -m json.tool

# 4. 创建商品
curl -s -b "$COOKIE_JAR" -X POST "$BASE/products" \
  -H "Content-Type: application/json" \
  -d '{"name":"答辩演示商品","slug":"demo-item","price":"88.00","stock":50}'

# 5. 未登录写操作应 401
curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE/products" \
  -H "Content-Type: application/json" -d '{"name":"x"}'
# 期望输出 401
步骤期望
列表code: 0,含 pagination
/me返回当前用户 email
创建status 201data.slug 正确
匿名 POSTHTTP 401,code: 40101

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。