Files
PythonLearn/05_web基础/5_6_HTTP接口调试与综合实践/README.md
T

11 KiB
Raw Blame History

第5-6课:HTTP接口调试与综合实践

一、本课定位

本课把客户端、服务器、URL、HTTP、状态码、JSON和RESTful API组合成一个可运行的本地闭环。示例使用Python标准库搭建临时图书接口,只服务于协议观察,不替代下一阶段的FastAPI。

二、本课目标

完成本课后,你能够:

  1. 启动一个只监听本机的临时HTTP服务;
  2. 使用客户端代码发送GET和POST请求;
  3. 同时检查状态码、响应头和JSON响应体;
  4. 识别JSON格式错误、字段错误和资源不存在;
  5. 设计图书管理系统的RESTful API;
  6. 说明HTTP知识如何迁移到FastAPI。

三、前置知识

  • 客户端、服务器和请求响应流程;
  • URL、HTTP方法、请求头和请求体;
  • 常见状态码;
  • JSON转换与RESTful API设计;
  • Python类、异常、线程和上下文管理器的基本阅读能力。

线程(Thread)表示同一程序中的一条执行路线。本课只用后台线程让服务器等待请求,主线程同时充当客户端;不展开并发编程细节。

本课示例使用了前面阶段学过的类、异常和上下文管理器,但只要求按调用流程阅读,不重新讲解这些Python语法。

四、为什么现在才启动真实HTTP服务

前五课分别拆开观察了通信角色、URL、请求、响应、JSON和接口设计。如果一开始就使用框架,路由、自动校验和自动序列化会让新手看到结果,却不容易分清是谁完成了哪一步。

本课使用标准库建立一个最小闭环:

  1. 程序在本机启动HTTP服务器;
  2. 客户端向它发送真实HTTP请求;
  3. 服务器读取路径、请求头和请求体;
  4. 服务器修改内存数据;
  5. 服务器返回状态码、响应头和JSON;
  6. 客户端读取并输出响应;
  7. 全部测试结束后关闭服务器。

它是教学服务器,不是下一阶段要长期使用的项目框架。

五、先认识本课的新名词

名词 本课中的含义
localhost 表示自己的计算机,常对应127.0.0.1
监听(Listen) 服务器等待某个地址和端口上的请求
随机端口 传入端口0,由操作系统选择当前可用端口
处理器(Handler) 收到请求后负责读取并生成响应的代码
后台线程 在同一程序中持续运行服务器,让主流程可以发送请求
内存数据 只存在于当前程序运行期间,结束后不会保存
超时(Timeout) 等待超过限定时间后停止继续等待

127.0.0.1只指向本机。绑定该地址可以避免本课没有认证的服务被局域网其他设备访问。

六、完整调用链

send_request()
    ↓ HTTP请求
BookApiHandler
    ↓ 路径匹配、JSON解析、字段校验
内存图书列表
    ↓ JSON响应
send_request()读取状态码、响应头和响应体

示例绑定127.0.0.1,只接受本机连接;端口传入0,由操作系统选择当前可用端口,避免与现有服务冲突。数据只保存在内存中,程序结束即消失。

同一个Python程序同时扮演两种角色:

  • ThreadingHTTPServer和BookApiHandler是服务器;
  • send_request()是客户端;
  • 它们通过本机网络连接交换HTTP消息,不是普通函数直接传参。

七、服务器如何启动

server = ThreadingHTTPServer(("127.0.0.1", 0), BookApiHandler)

这行代码只创建服务器对象,还没有开始持续等待请求。地址元组中:

  • 127.0.0.1限制为本机;
  • 0请求操作系统分配端口;
  • BookApiHandler指定收到请求后由哪个类处理。

随后创建后台线程并执行serve_forever()。名称表示服务器会持续等待,直到程序调用shutdown()。

八、服务端重点

BaseHTTPRequestHandler按请求方法调用do_GET()或do_POST()。send_json()负责:

  1. 将Python对象序列化为JSON;
  2. 编码成UTF-8字节;
  3. 写入状态码和响应头;
  4. 按字节数计算Content-Length;
  5. 写出响应体。

中文字符的字符数量与UTF-8字节数量不同,因此必须对编码后的response_body调用len()。

8.1 GET请求

客户端请求GET /books时,标准库调用do_GET():

  1. 检查self.path是否等于/books;
  2. 路径匹配时返回200和图书列表;
  3. 路径不匹配时返回404和错误JSON。

8.2 POST请求

客户端请求POST /books时,标准库调用do_POST():

  1. 检查路径;
  2. 从Content-Length得知需要读取多少字节;
  3. 读取请求体并按UTF-8解码;
  4. 使用json.loads()解析JSON;
  5. 校验title是否为非空字符串;
  6. 创建新图书并加入内存列表;
  7. 返回201、新图书JSON和Location响应头。

JSON语法错误返回400,字段不符合要求返回422,未知路径返回404。三个结果代表不同问题。

九、客户端重点

urllib.request.Request创建请求,urlopen()发送请求。遇到4xx或5xx时,标准库会抛出HTTPError,但错误对象仍包含状态码和响应体,需要读取后再判断原因。

接口调试不能只看响应体。至少检查:

  • 请求方法与完整URL;
  • 请求头和请求体;
  • HTTP状态码;
  • 响应Content-Type;
  • JSON响应结构;
  • 服务端数据是否按预期变化。

客户端发送JSON前,需要完成与服务器相反的转换:

Python字典 → JSON字符串 → UTF-8字节 → HTTP请求体

客户端收到响应后再反向处理:

HTTP响应体字节 → UTF-8字符串 → 显示或解析JSON

示例为保持输出直观,只显示JSON字符串。实际业务客户端通常还会使用json.loads()转换成对象。

十、程序完整执行顺序

  1. 创建服务器对象;
  2. 创建并启动后台线程;
  3. 从服务器对象取得操作系统分配的端口;
  4. 第一次GET查询初始列表;
  5. POST创建“HTTP接口实践”;
  6. 第二次GET验证列表已经变化;
  7. GET未知路径验证404;
  8. 无论中间是否出错,finally都会关闭服务器;
  9. 主线程等待后台线程结束;
  10. 程序输出“本地服务已关闭”。

这里先新增再查询,是为了验证POST不只是返回201,还真实改变了当前程序的内存状态。

十一、运行方法

cd D:\Code\Python\05_web基础\5_6_HTTP接口调试与综合实践
python local_book_api_example.py

十二、预期结果

本地服务已启动:http://127.0.0.1:随机端口号
GET http://127.0.0.1:相同端口/books -> 200
响应体:{"items": [{"id": 1, "title": "Python入门"}]}
POST http://127.0.0.1:相同端口/books -> 201
响应体:{"id": 2, "title": "HTTP接口实践"}
GET http://127.0.0.1:相同端口/books -> 200
响应体中包含两本图书
GET http://127.0.0.1:相同端口/missing -> 404
响应体:{"message": "资源不存在"}
本地服务已关闭。

端口号每次不同是正常现象。第二次查询应包含新创建的“HTTP接口实践”。

十三、怎样判断测试真正通过

不要只检查程序是否结束,应逐项确认:

  1. 四次请求使用同一个随机端口;
  2. 第一次GET只有初始图书;
  3. POST状态码是201而不是200;
  4. POST响应中生成了编号2;
  5. 第二次GET包含编号1和编号2;
  6. 未知路径返回404;
  7. 最后一行确认服务器已关闭;
  8. 再运行一次时仍从一条初始数据开始。

最后一项说明数据只在内存中,没有持久保存,也说明示例可重复运行。

十四、使用接口调试工具

示例服务会自动结束,不适合手工操作。完成practice.py时,可以让服务等待键盘输入后再关闭,然后使用Postman、Apifox或IDE内置HTTP客户端发送请求:

GET http://127.0.0.1:端口/books
POST http://127.0.0.1:端口/books
Content-Type: application/json

{"title": "Web实践", "author": "张三"}

不要把服务绑定到0.0.0.0,本课没有实现认证和生产安全配置。

调试工具中的请求配置通常分为:

  1. 选择HTTP方法;
  2. 输入完整URL;
  3. 配置请求头;
  4. 选择JSON请求体并输入数据;
  5. 发送请求;
  6. 查看状态码、耗时、响应头和响应体。

不要只复制响应内容,还要记录发送了什么请求,否则别人无法复现问题。

十五、图书管理API设计草案

方法 路径 成功状态 作用
GET /books 200 分页查询图书
GET /books/{book_id} 200 查询图书详情
POST /books 201 新增图书
PATCH /books/{book_id} 200 修改部分字段
DELETE /books/{book_id} 204 删除图书

常见失败包括400格式错误、404图书不存在、409编号冲突和422字段校验失败。实际项目还要处理认证、权限、日志、数据库事务和并发。

十六、与FastAPI的衔接

下一阶段会由FastAPI代替底层标准库代码:

  • 路由装饰器代替手工判断路径和方法;
  • Pydantic模型代替手工字段校验;
  • 框架自动序列化JSON并生成接口文档;
  • 异常处理器统一生成错误响应。

不会改变的是HTTP层的设计:方法、路径、参数、状态码、请求结构和响应结构仍需开发者决定。

十七、常见错误

17.1 服务启动后程序不结束

服务器会持续等待请求。必须调用shutdown()和server_close(),并等待后台线程结束。

17.2 只测试成功情况

至少覆盖未知路径、无效JSON和缺少必填字段,否则无法确认错误边界。

17.3 把标准库示例当作生产服务器

本例只用于教学,没有完整认证、限流、日志、安全加固和部署能力。

17.4 把服务绑定到0.0.0.0

这会让服务监听所有可用网络接口。本课没有认证,不应扩大访问范围。

17.5 按字符数计算Content-Length

HTTP传输的是字节。包含中文时必须先编码,再计算字节长度。

17.6 忘记处理HTTPError

标准库遇到4xx和5xx会抛出HTTPError。错误对象中仍有响应体,应读取并用于定位问题。

17.7 端口不同就认为结果错误

本课故意使用随机可用端口。只要同一次运行中的请求端口一致即可。

十八、课堂练习

打开practice.py,完成图书管理接口设计、服务端实现和客户端调试。练习不提供答案或代码骨架。

十九、本课小结

一个接口是否正确,要从客户端请求、服务器处理和HTTP响应三个方向共同验证。框架会减少底层代码,但不会代替接口语义和业务规则设计。

二十、验收标准

  • 本地服务能自动启动和关闭;
  • GET、POST和404请求输出符合预期;
  • JSON中文编码与Content-Length正确;
  • 能设计完整的图书管理接口;
  • 能说明本阶段知识与FastAPI的对应关系;
  • 能完整描述GET和POST在服务器中的处理顺序;
  • 能从请求方法、URL、状态码、响应头、响应体和数据变化六方面调试接口;
  • 完成practice.py综合练习。