Files

第5-5课:RESTful API设计

一、本课目标

完成本课后,你能够:

  1. 理解资源、资源标识和资源表现形式;
  2. 解释表述性状态转移(Representational State Transfer,REST)的基本思想;
  3. 使用路径、HTTP方法和状态码共同描述一个接口;
  4. 区分列表接口、详情接口和业务动作接口;
  5. 为图书管理系统设计一组结构一致的RESTful API。

二、前置知识

  • 已学过URL和HTTP请求方法;
  • 已学过HTTP响应和常用状态码;
  • 已学过JSON请求体和响应体;
  • 不要求接触过FastAPI、Django或任何接口框架。

三、先理解“资源”

资源(Resource)是系统希望通过接口管理的业务对象,例如图书、作者、用户和订单。它不是只指数据库中的一行,也不是只指一个Python对象,而是从接口使用者角度看到的业务事物。

一本编号为10的图书可以用路径表示:

/books/10

这里:

  • books表示图书资源集合;
  • 10用于标识集合中的一本图书;
  • 返回的JSON是这本图书在当前时刻的一种表现形式。

同一个资源可以有JSON、HTML等不同表现形式,但后端API通常返回JSON。

四、REST与RESTful是什么

REST是一种Web接口架构风格,不是协议、框架或Python库。RESTful表示一个接口的设计遵循REST的资源化思想。

入门阶段先掌握三条核心规则:

  1. 路径主要描述资源,优先使用名词;
  2. HTTP方法描述想对资源做什么;
  3. HTTP状态码描述处理结果。

因此创建图书通常写成:

POST /books → 201 Created

而不是:

POST /createBook → 200 OK

后一种不一定不能运行,但它没有充分利用HTTP已有的语义。

五、从一个接口开始设计

设计接口时按以下顺序思考:

  1. 管理的业务资源是什么?这里是图书;
  2. 是操作集合还是单个资源?/books是集合,/books/10是单个资源;
  3. 操作意图是什么?查询、创建、修改还是删除;
  4. 输入放在哪里?路径参数、查询参数或JSON请求体;
  5. 成功返回什么状态码和数据?
  6. 可能失败的情况有哪些?

六、图书资源接口

方法 路径 输入位置 成功状态 作用
GET /books 查询参数 200 查询图书列表
GET /books/{book_id} 路径参数 200 查询一本图书
POST /books JSON请求体 201 创建图书
PUT /books/{book_id} JSON请求体 200或204 整体替换图书
PATCH /books/{book_id} JSON请求体 200或204 修改部分字段
DELETE /books/{book_id} 路径参数 204 删除图书

表中的{book_id}是路径参数占位符。真实请求会写成/books/10,花括号不会原样发送。

七、列表、详情与分页

列表接口操作资源集合:

GET /books?keyword=Python&page=1&page_size=20
  • keyword负责筛选;
  • page表示第几页;
  • page_size表示每页多少条。

一种清晰的列表响应是:

{
  "items": [
    {"id": 10, "title": "Python入门"}
  ],
  "page": 1,
  "page_size": 20,
  "total": 1
}

详情接口操作单个资源:

GET /books/10

存在时返回200和图书JSON;不存在时返回404,而不是返回200和空字符串。

八、新增接口的完整设计

请求:

POST /books HTTP/1.1
Content-Type: application/json

{"title": "Web基础", "price": 59.00}

成功响应:

HTTP/1.1 201 Created
Location: /books/10
Content-Type: application/json

{"id": 10, "title": "Web基础", "price": 59.00}

Location响应头告诉客户端新资源的地址。常见失败包括JSON格式错误、字段不合规则和图书编号冲突。

九、PUT与PATCH的区别

  • PUT通常表示用请求内容整体替换资源的可修改状态;
  • PATCH通常表示只修改请求中出现的字段。

例如只修改价格时,PATCH请求体可以是:

{"price": 69.00}

项目必须明确PUT缺少字段时如何处理。不要仅凭方法名称猜测,接口文档需要写清楚。

十、业务动作如何设计

不是所有业务都能自然表示为基础增删改查。例如“取消订单”是一个带业务规则的动作。

可以把取消记录理解成订单下的子资源:

POST /orders/10/cancellations

也有项目使用:

POST /orders/10/cancel

RESTful不是机械规则。选择能够清楚表达业务、便于权限与审计、并且全项目一致的方案更重要。

十一、错误响应也需要设计

同一个详情接口可能产生不同结果:

情况 状态码 响应含义
查询成功 200 返回资源
编号格式错误 400或422 客户端输入不符合要求
图书不存在 404 找不到指定资源
未登录 401 需要完成身份认证
没有权限 403 身份已知但禁止访问
后端未知异常 500 服务器未能完成处理

错误响应应保持固定结构,例如包含错误代码和面向调用者的消息,但不能泄露数据库密码或程序堆栈。

十二、完整示例

rest_api_design_example.py不建立网络连接,而是把一组接口设计保存为Python列表并输出。这样可以把注意力放在方法、路径和状态码的组合上。

十三、运行方法

cd D:\Code\Python\05_web基础\5_5_RESTful_API设计
python rest_api_design_example.py

预期输出包含五条图书接口和一个带筛选、分页参数的列表请求示例。

十四、代码执行顺序

  1. main()调用build_book_api_design();
  2. 函数返回由多个字典组成的列表;
  3. print_api_design()逐条读取接口;
  4. 每条接口按统一列顺序输出;
  5. 最后输出列表筛选示例。

示例中的数据结构只是“接口设计表”,不是服务器,也不会真的新增或删除图书。

十五、常见错误

15.1 路径全部使用动词

/getBooks、/createBook和/deleteBook会把HTTP方法已有的意图重复写入路径,并使接口越来越不一致。

15.2 用GET修改数据

浏览器、缓存或爬虫可能自动访问GET链接。GET应当用于读取,不能因为输入方便就用它删除数据。

15.3 列表接口一次返回全部数据

数据增长后会拖慢数据库、服务器和客户端。列表接口应尽早明确分页规则和最大每页数量。

15.4 只设计成功响应

客户端真正需要处理的往往是校验失败、资源不存在、状态冲突和权限不足。接口设计必须包含失败边界。

15.5 认为RESTful只有唯一答案

REST提供设计思想,复杂业务动作可能有多种合理表达。项目内一致、语义清楚和文档完整比形式上的绝对统一更重要。

十六、课堂练习

打开practice.py,完成作者资源、订单业务动作和反例改造练习。练习文件不含参考答案。

十七、本课小结

RESTful API围绕资源组织路径,用HTTP方法表达操作,用状态码表达结果。设计一个接口时,不仅要考虑成功路径,还要明确输入位置、错误状态和响应结构。

十八、验收标准

  • 能解释资源、集合路径和详情路径;
  • 能组合路径、方法和状态码;
  • 能区分查询参数、路径参数和JSON请求体;
  • 能解释PUT与PATCH的基本差异;
  • 能设计包含失败结果的图书接口;
  • 示例可独立运行;
  • 完成practice.py中的练习。