第 94 课 - 项目实战:设计并实现一个 RESTful API 服务
学习目标
完成本课学习后,你将能够:
- 理解 RESTful API 的核心设计思想与规范。
- 使用 Flask 框架快速搭建一个基础的 API 服务骨架。
- 实现 资源的创建(POST)、读取(GET)、更新(PUT)和删除(DELETE)操作。
- 集成 SQLAlchemy 进行数据持久化,并使用 Flask-Marshmallow 进行数据序列化。
- 掌握 基本的错误处理和状态码使用,编写可维护、可测试的 API。
核心概念
什么是 RESTful API? 想象一下,你正在和一家大型图书馆的管理员打交道。你告诉他你想要什么("给我《三体》这本书"),他就会去书架上找,然后给你。你不需要知道他具体是怎么走过去的,也不需要知道书架是什么颜色。这就是 REST (Representational State Transfer) 的核心思想:客户端和服务器之间通过资源的"表述"进行交互,并且通信是无状态的。
在 Web API 中:
- 资源 (Resource):你想要操作的东西,比如用户、文章、商品。在 URL 中通常用名词表示(如
/users,/articles/1)。 - 表述 (Representation):资源的具体表现形式,通常是 JSON 格式的数据。
- 动词 (Verb):HTTP 方法(
GET,POST,PUT,DELETE)定义了你要对资源做什么。
关键原则:
- 无状态 (Stateless):每个请求必须包含所有必要信息,服务器不保存客户端的上下文。这就像每次去图书馆都带着完整的借书证和书名。
- 统一接口 (Uniform Interface):通过标准的 HTTP 方法和一致的 URL 结构来操作资源。
- 可缓存 (Cacheable):服务器的响应应明确指示客户端是否可以缓存它,以提高性能。
代码示例
我们将使用轻量级的 Flask 框架来构建 API,配合 SQLAlchemy ORM 和 Flask-Marshmallow 进行数据序列化。
步骤 1:安装依赖
pip install Flask Flask-SQLAlchemy Flask-Marshmallow marshmallow-sqlalchemy
步骤 2:创建完整的项目文件 app.py
# app.py - 一个完整的用户管理 RESTful API 服务
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
import os
# 初始化 Flask 应用
app = Flask(__name__)
basedir = os.path.abspath(os.path.dirname(__file__))
# 配置数据库 (SQLite)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'db.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 初始化数据库和序列化工具
db = SQLAlchemy(app)
ma = Marshmallow(app)
# 定义用户数据模型
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
age = db.Column(db.Integer, nullable=True)
def __repr__(self):
return f'<User {self.username}>'
# 创建 Marshmallow 序列化 Schema,用于将模型对象转为 JSON
class UserSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = User
load_instance = True # 允许从 JSON 反序列化创建模型实例
user_schema = UserSchema() # 单个用户
users_schema = UserSchema(many=True) # 用户列表
# 在第一次请求前创建数据库表
@app.before_first_request
def create_tables():
db.create_all()
# 路由:创建新用户 (POST /users)
@app.route('/users', methods=['POST'])
def create_user():
try:
# 从 JSON 请求体中获取数据,并反序列化为 User 对象
user = user_schema.load(request.json)
db.session.add(user)
db.session.commit()
# 序列化新用户对象并返回,状态码 201 表示"已创建"
return user_schema.dump(user), 201
except Exception as e:
db.session.rollback()
# 返回错误信息和状态码 400 表示"错误的请求"
return jsonify({'error': str(e)}), 400
# 路由:获取所有用户 (GET /users)
@app.route('/users', methods=['GET'])
def get_users():
all_users = User.query.all()
# 序列化用户列表并返回
return users_schema.dump(all_users)
# 路由:获取单个用户 (GET /users/<id>)
@app.route('/users/<int:id>', methods=['GET'])
def get_user(id):
user = User.query.get_or_404(id) # 如果找不到,返回 404
return user_schema.dump(user)
# 路由:更新用户信息 (PUT /users/<id>)
@app.route('/users/<int:id>', methods=['PUT'])
def update_user(id):
user = User.query.get_or_404(id)
try:
# 使用请求数据更新用户对象
updated_user = user_schema.load(request.json, instance=user, partial=True)
db.session.commit()
return user_schema.dump(updated_user)
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 400
# 路由:删除用户 (DELETE /users/<id>)
@app.route('/users/<int:id>', methods=['DELETE'])
def delete_user(id):
user = User.query.get_or_404(id)
db.session.delete(user)
db.session.commit()
# 返回状态码 204 表示"无内容",表示删除成功但无需返回实体
return '', 204
# 错误处理:自定义 404 错误响应
@app.errorhandler(404)
def not_found(error):
return jsonify({'error': 'Resource not found'}), 404
# 启动应用
if __name__ == '__main__':
app.run(debug=True, port=5001)
步骤 3:运行并测试 API
- 保存上述代码为
app.py并运行:python app.py - 使用 Postman 或 curl 等工具测试。
- 创建用户 (POST):
curl -X POST http://localhost:5001/users \ -H "Content-Type: application/json" \ -d '{"username": "alice", "email": "[email protected]", "age": 30}' - 获取所有用户 (GET):
curl http://localhost:5001/users - 更新用户 (PUT):
curl -X PUT http://localhost:5001/users/1 \ -H "Content-Type: application/json" \ -d '{"age": 31}' - 删除用户 (DELETE):
curl -X DELETE http://localhost:5001/users/1
- 创建用户 (POST):
实践练习
练习 1 (基础):添加"书籍"资源
在现有 app.py 基础上,创建一个 Book 模型(字段:id, title, author, isbn),并实现其完整的 CRUD API(/books, /books/<id>)。
练习 2 (进阶):建立用户与书籍的关联
为 User 模型添加一个 favorite_books 字段,表示该用户收藏的书籍列表(多对多关系)。实现 API:GET /users/<id>/books 来获取指定用户收藏的所有书籍。
练习 3 (挑战):添加查询参数与分页
为 GET /users 接口添加查询参数,例如 ?username=alice 来按用户名过滤。同时,实现分页功能,例如 ?page=1&per_page=10。
常见错误
- 忘记设置
Content-Type头:当发送 JSON 数据时,务必在请求头中设置Content-Type: application/json,否则 Flask 无法正确解析request.json。 - 误用 HTTP 方法:创建用
POST,读取用GET,全量更新用PUT,删除用DELETE。部分更新通常用PATCH。使用错误的方法会导致 API 语义混乱。 - 数据库会话未提交或回滚:在
try...except块中,如果操作失败,一定要调用db.session.rollback()来撤销当前事务,否则可能导致数据库锁定。 - 状态码使用不当:
200 OK是通用成功,201 Created更适合资源创建成功,204 No Content适合删除成功,404 Not Found表示资源不存在,400 Bad Request表示客户端请求有误。正确的状态码能让 API 更加专业。
小结
在本课的实战中,我们从零开始构建了一个标准的 RESTful API 服务,关键收获如下:
- REST 设计:遵循资源导向、HTTP 方法语义、无状态通信的核心原则。
- Flask 框架:利用其轻量和灵活的特点,快速定义路由和视图函数。
- 数据层:使用 SQLAlchemy 进行对象关系映射,简化数据库操作。
- 数据转换:使用 Marshmallow 将复杂的模型实例与简单的 JSON 格式进行双向转换。
- 健壮性:通过
try...except和恰当的 HTTP 状态码,使 API 具备基本的错误处理能力。
这个项目为你打下了坚实的基础。你可以以此为模板,扩展出更复杂的 API,比如添加认证、权限控制、缓存等,来构建生产级别的应用。