零基础入门Python12|HTTP请求响应:用标准库写第一个Web服务
本篇图解:一次请求的完整链路
读代码时可以沿着这条链路定位:输入错误看校验,权限错误看依赖,数据错误看服务和数据库。
一、上一篇课后练习讲解
参考命令顺序:
gitswitch-c feature/priority git add task_manager/models.py task_manager/service.py git commit-m"feat: support task priority"# 制造未提交修改后查看gitdiff# 确认不要该修改后恢复git restore main.py gitswitchmain git merge feature/priority git log--oneline--graph--all数据流是:编辑器修改文件形成工作区变化;git add把选定变化复制到暂存区;git commit把暂存区形成不可变提交。restore根据选择恢复工作区或暂存区。
可执行验收答案
在任务管理器仓库根目录逐条执行,确认每一步的状态:
gitswitch-c feature/priority git add task_manager/models.py task_manager/service.py git commit-m"feat: support task priority"git status--short git log-1--oneline gitswitchmain git merge--ff-only feature/priority git log--oneline--graph--decorate-3git status --short为空表示工作区干净,最后一条日志应包含新提交。若git merge --ff-only提示无法快进,不要强行 reset;先执行git log --graph --all看是否有分叉,再用普通 merge 或 rebase,并在提交信息中记录原因。git restore main.py只会丢弃未提交的工作区修改,执行前先用git diff main.py确认内容确实不要。
二、本篇成果
运行一个真实HTTP服务,用浏览器和Python客户端发出请求,观察方法、路径、请求头、状态码、响应头和JSON正文。后面的所有框架都只是更方便地处理同一种HTTP交换。
三、一次请求包含什么
浏览器请求:
GET /api/health HTTP/1.1 Host: 127.0.0.1:8000 Accept: application/json- GET是方法;
- /api/health是路径;
- HTTP/1.1是协议版本;
- Host、Accept是请求头;
- GET通常没有请求体。
服务器响应:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Content-Length: 40 {"status":"ok","message":"服务正常"}200是状态码;响应头描述正文;空行之后是响应体。
四、常用方法和状态码
- GET读取资源,不应改变服务器数据;
- POST创建资源;
- PUT整体替换;
- PATCH部分修改;
- DELETE删除。
常见状态码:
- 200查询或修改成功;
- 201创建成功;
- 204删除成功且无正文;
- 400请求格式或业务参数错误;
- 401尚未认证;
- 403已认证但无权操作;
- 404资源不存在;
- 409与当前数据冲突;
- 500服务器未处理的错误。
五、完整HTTP服务
保存为server.py:
fromhttp.serverimportBaseHTTPRequestHandler,HTTPServerimportjsonclassHandler(BaseHTTPRequestHandler):defsend_json(self,status_code,data):body=json.dumps(data,ensure_ascii=False).encode("utf-8")self.send_response(status_code)self.send_header("Content-Type","application/json; charset=utf-8")self.send_header("Content-Length",str(len(body)))self.end_headers()self.wfile.write(body)defdo_GET(self):ifself.path=="/api/health":self.send_json(200,{"status":"ok","message":"服务正常"},)returnifself.path=="/api/tasks":self.send_json(200,{"items":[{"id":1,"title":"学习HTTP","done":False},],},)returnself.send_json(404,{"code":"NOT_FOUND","message":"接口不存在"},)server=HTTPServer(("127.0.0.1",8000),Handler)print("服务已启动:http://127.0.0.1:8000")server.serve_forever()启动:
python server.py访问:
http://127.0.0.1:8000/api/health http://127.0.0.1:8000/api/tasks http://127.0.0.1:8000/not-found六、用Python客户端发请求
另开一个终端,保存client.py:
fromurllib.errorimportHTTPErrorfromurllib.requestimporturlopen urls=["http://127.0.0.1:8000/api/health","http://127.0.0.1:8000/api/tasks","http://127.0.0.1:8000/not-found",]forurlinurls:try:withurlopen(url)asresponse:body=response.read().decode("utf-8")print(response.status,response.headers["Content-Type"],body)exceptHTTPErroraserror:body=error.read().decode("utf-8")print(error.code,body)客户端同时检查状态码、Content-Type和正文。404并不代表网络失败,而是服务器明确回复“资源不存在”。
七、URL、查询参数和JSON
URL可以包含查询参数:
/api/tasks?page=1&size=10&done=false问号后是查询字符串,————于分页、筛选和排序。创建任务通常使用POST和JSON请求体:
{"title":"学习HTTP","priority":"high"}标准库手写POST解析很繁琐,这正是Web框架提供路由、校验和JSON处理的原因。
八、本篇验收
- 服务能在127.0.0.1:8000启动;
- health返回200和JSON;
- tasks返回列表;
- 未知路径返回404而不是200;
- Content-Type声明JSON和UTF-8;
- 客户端能区分成功与HTTPError;
- 能指出请求和响应各部分。
九、课后练习
为标准库服务增加GET /api/tasks/1,返回单个任务;GET /api/tasks/999返回404。再记录浏览器请求中User-Agent请求头,并在服务端打印出来。下一篇会把这些路径整理成完整REST接口契约。
实战补充:HTTP 调试脚本
把手工 curl 固化成脚本,状态码不符合预期时立即退出,避免测试人员只看到了响应正文。
importrequestsdefexpect(method,url,status,**kwargs):response=requests.request(method,url,timeout=3,**kwargs)print(method,url,response.status_code)assertresponse.status_code==status,response.textreturnresponse expect('GET','http://127.0.0.1:8000/health',200)课后练习:补充 422、404 和 500 场景,保存响应体中的 request_id,下一篇把这些请求映射到 REST 资源。
本篇结束:完整模块文件
本节不是代码片段,而是本篇结束时该模块的完整版本。请先备份旧文件,再整体替换;替换后重新运行本篇命令和测试。阅读时重点看本篇新增的函数、事务边界和错误处理,未涉及的代码先不要自行删减。
本篇完整示例
GET /api/books?page=1 HTTP/1.1 Host: example.com Accept: application/json HTTP/1.1 200 OK Content-Type: application/json