# 第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的图书可以用路径表示: ```text /books/10 ``` 这里: - `books`表示图书资源集合; - `10`用于标识集合中的一本图书; - 返回的JSON是这本图书在当前时刻的一种表现形式。 同一个资源可以有JSON、HTML等不同表现形式,但后端API通常返回JSON。 ## 四、REST与RESTful是什么 REST是一种Web接口架构风格,不是协议、框架或Python库。RESTful表示一个接口的设计遵循REST的资源化思想。 入门阶段先掌握三条核心规则: 1. 路径主要描述资源,优先使用名词; 2. HTTP方法描述想对资源做什么; 3. HTTP状态码描述处理结果。 因此创建图书通常写成: ```text POST /books → 201 Created ``` 而不是: ```text 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`,花括号不会原样发送。 ## 七、列表、详情与分页 列表接口操作资源集合: ```text GET /books?keyword=Python&page=1&page_size=20 ``` - `keyword`负责筛选; - `page`表示第几页; - `page_size`表示每页多少条。 一种清晰的列表响应是: ```json { "items": [ {"id": 10, "title": "Python入门"} ], "page": 1, "page_size": 20, "total": 1 } ``` 详情接口操作单个资源: ```text GET /books/10 ``` 存在时返回200和图书JSON;不存在时返回404,而不是返回200和空字符串。 ## 八、新增接口的完整设计 请求: ```http POST /books HTTP/1.1 Content-Type: application/json {"title": "Web基础", "price": 59.00} ``` 成功响应: ```http 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请求体可以是: ```json {"price": 69.00} ``` 项目必须明确PUT缺少字段时如何处理。不要仅凭方法名称猜测,接口文档需要写清楚。 ## 十、业务动作如何设计 不是所有业务都能自然表示为基础增删改查。例如“取消订单”是一个带业务规则的动作。 可以把取消记录理解成订单下的子资源: ```text POST /orders/10/cancellations ``` 也有项目使用: ```text POST /orders/10/cancel ``` RESTful不是机械规则。选择能够清楚表达业务、便于权限与审计、并且全项目一致的方案更重要。 ## 十一、错误响应也需要设计 同一个详情接口可能产生不同结果: | 情况 | 状态码 | 响应含义 | |---|---:|---| | 查询成功 | 200 | 返回资源 | | 编号格式错误 | 400或422 | 客户端输入不符合要求 | | 图书不存在 | 404 | 找不到指定资源 | | 未登录 | 401 | 需要完成身份认证 | | 没有权限 | 403 | 身份已知但禁止访问 | | 后端未知异常 | 500 | 服务器未能完成处理 | 错误响应应保持固定结构,例如包含错误代码和面向调用者的消息,但不能泄露数据库密码或程序堆栈。 ## 十二、完整示例 `rest_api_design_example.py`不建立网络连接,而是把一组接口设计保存为Python列表并输出。这样可以把注意力放在方法、路径和状态码的组合上。 ## 十三、运行方法 ```powershell 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`中的练习。