94·模块十八:综合项目实战高级

项目实战:设计并实现一个 RESTful API 服务

projectapiflaskdjango-rest

第 94 课 - 项目实战:设计并实现一个 RESTful API 服务

学习目标

完成本课学习后,你将能够:

  1. 理解 RESTful API 的核心设计思想与规范。
  2. 使用 Flask 框架快速搭建一个基础的 API 服务骨架。
  3. 实现 资源的创建(POST)、读取(GET)、更新(PUT)和删除(DELETE)操作。
  4. 集成 SQLAlchemy 进行数据持久化,并使用 Flask-Marshmallow 进行数据序列化。
  5. 掌握 基本的错误处理和状态码使用,编写可维护、可测试的 API。

核心概念

什么是 RESTful API? 想象一下,你正在和一家大型图书馆的管理员打交道。你告诉他你想要什么("给我《三体》这本书"),他就会去书架上找,然后给你。你不需要知道他具体是怎么走过去的,也不需要知道书架是什么颜色。这就是 REST (Representational State Transfer) 的核心思想:客户端和服务器之间通过资源的"表述"进行交互,并且通信是无状态的。

在 Web API 中:

  • 资源 (Resource):你想要操作的东西,比如用户、文章、商品。在 URL 中通常用名词表示(如 /users, /articles/1)。
  • 表述 (Representation):资源的具体表现形式,通常是 JSON 格式的数据。
  • 动词 (Verb):HTTP 方法(GET, POST, PUT, DELETE)定义了你要对资源做什么。

关键原则:

  1. 无状态 (Stateless):每个请求必须包含所有必要信息,服务器不保存客户端的上下文。这就像每次去图书馆都带着完整的借书证和书名。
  2. 统一接口 (Uniform Interface):通过标准的 HTTP 方法和一致的 URL 结构来操作资源。
  3. 可缓存 (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

  1. 保存上述代码为 app.py 并运行:python app.py
  2. 使用 Postmancurl 等工具测试。
    • 创建用户 (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
      

实践练习

练习 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

常见错误

  1. 忘记设置 Content-Type:当发送 JSON 数据时,务必在请求头中设置 Content-Type: application/json,否则 Flask 无法正确解析 request.json
  2. 误用 HTTP 方法:创建用 POST,读取用 GET,全量更新用 PUT,删除用 DELETE。部分更新通常用 PATCH。使用错误的方法会导致 API 语义混乱。
  3. 数据库会话未提交或回滚:在 try...except 块中,如果操作失败,一定要调用 db.session.rollback() 来撤销当前事务,否则可能导致数据库锁定。
  4. 状态码使用不当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,比如添加认证、权限控制、缓存等,来构建生产级别的应用。

练习编辑器

python
Loading...

继续学习

完成本课后,建议继续学习下一课「项目实战:开发一个博客系统(Django/Flask)」 以巩固所学知识。