← 开发笔记

用小型 Python 本地服务器测试 HTTP 状态码

仅有状态码不能证明响应可用。客户端可能收到状态为 200 的 HTML,也可能收到成功的 204,却没有可解码的正文。本地模拟服务可以在不依赖外部响应服务的情况下复现这些情形。

本例使用 Python 3.11 或更高版本,只依赖标准库。服务绑定到 127.0.0.1,为一小组固定路由实现 GET。这是开发用的模拟服务,而非应用服务器。

处理流程

处理器先选择状态与表示,再写入响应头。204 分支不含正文或 Content-Length,其他路由写入声明的字节数。

处理器先选择状态与表示,再写入响应头。204 分支不含正文或 Content-Length,其他路由写入声明的字节数。

将完整服务器保存为 status_server.py

import argparse
import json
import re
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlsplit

SUPPORTED = {200, 201, 204, 400, 404, 429, 500, 503}

class StatusHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        path = urlsplit(self.path).path
        match = re.fullmatch(r"/status/([0-9]{3})", path)
        requested = int(match.group(1)) if match else None
        status = requested if requested in SUPPORTED else 404
        content_type = "application/json; charset=utf-8"
        body = json.dumps({"status": status}).encode("utf-8")
        if path == "/html/200":
            status = 200
            content_type = "text/html; charset=utf-8"
            body = b"<html><body>not JSON</body></html>"
        self.send_response(status)
        if status == 204:
            self.end_headers()
            return
        self.send_header("Content-Type", content_type)
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, format, *args):
        pass

def make_server(port=8089):
    return ThreadingHTTPServer(("127.0.0.1", port), StatusHandler)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--port", type=int, default=8089)
    args = parser.parse_args()
    if not 0 <= args.port <= 65535:
        parser.error("port must be from 0 through 65535")
    with make_server(args.port) as server:
        print(f"http://127.0.0.1:{server.server_port}", flush=True)
        try:
            server.serve_forever()
        except KeyboardInterrupt:
            pass

Python HTTP 服务器 API提供请求处理与线程化监听器。不受支持的路径或状态码会返回模拟服务的 JSON 404。路由 /html/200 刻意以成功状态返回 HTML。

在一个终端启动服务器,再在另一个终端发送请求:

python3 status_server.py --port 8089
curl -i http://127.0.0.1:8089/status/500
curl -i http://127.0.0.1:8089/html/200
curl -i http://127.0.0.1:8089/status/204

使用端口 0 可让操作系统选择空闲端口,服务器会打印实际 URL。按 Ctrl+C 停止。这些 curl 命令特意省略 --fail,以便同时检查错误响应。

请求 状态 表示
/status/200 200 包含状态 200 的 JSON
/html/200 200 HTML,而非 JSON
/status/500 500 包含状态 500 的 JSON
/status/204 204 没有正文,也没有 Content-Length

RFC 9110禁止在 204 响应中使用 Content-Length。正文是否可用,应与 2xx 检查分别处理。此模拟服务检查状态、媒体类型和消息定界,不模拟慢响应、TLS、认证或重试时序。关于客户端解码与错误信号,可继续阅读 WebClient 响应测试。

讨论