零基础入门Python12|HTTP请求响应:用标准库写第一个Web服务

零基础入门Python12|HTTP请求响应:用标准库写第一个Web服务

本篇图解:一次请求的完整链路

在这里插入图片描述

读代码时可以沿着这条链路定位:输入错误看校验,权限错误看依赖,数据错误看服务和数据库。

一、上一篇课后练习讲解

参考命令顺序:

git switch -c feature/priority
git add task_manager/models.py task_manager/service.py
git commit -m "feat: support task priority"

# 制造未提交修改后查看
git diff

# 确认不要该修改后恢复
git restore main.py

git switch main
git merge feature/priority
git log --oneline --graph --all

数据流是:编辑器修改文件形成工作区变化;git add把选定变化复制到暂存区;git commit把暂存区形成不可变提交。restore根据选择恢复工作区或暂存区。

可执行验收答案

在任务管理器仓库根目录逐条执行,确认每一步的状态:

git switch -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
git switch main
git merge --ff-only feature/priority
git log --oneline --graph --decorate -3

git 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:

from http.server import BaseHTTPRequestHandler, HTTPServer
import json


class Handler(BaseHTTPRequestHandler):
    def send_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)

    def do_GET(self):
        if self.path == "/api/health":
            self.send_json(
                200,
                {"status": "ok", "message": "服务正常"},
            )
            return

        if self.path == "/api/tasks":
            self.send_json(
                200,
                {
                    "items": [
                        {"id": 1, "title": "学习HTTP", "done": False},
                    ],
                },
            )
            return

        self.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:

from urllib.error import HTTPError
from urllib.request import urlopen


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",
]

for url in urls:
    try:
        with urlopen(url) as response:
            body = response.read().decode("utf-8")
            print(response.status, response.headers["Content-Type"], body)
    except HTTPError as error:
        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 固化成脚本,状态码不符合预期时立即退出,避免测试人员只看到了响应正文。

import requests

def expect(method, url, status, **kwargs):
    response = requests.request(method, url, timeout=3, **kwargs)
    print(method, url, response.status_code)
    assert response.status_code == status, response.text
    return response

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值