2026-09-20 · 8 min read

5.3 理解HTTP,手搓一个API

先不上框架:用手搓的方式学 HTTP 基础。语言会变、框架会变,HTTP 协议不会过时。

课时概要

先不上框架,把 HTTP 规范看明白,再用 Python 标准库 http.server 手搓一个 API(GET /api/profile 回 JSON)。一去回两段文本:请求行/头/空行/体,状态码家族,curl -v 和 F12 亲眼看到报文。为什么要手搓——因为杂活人人一样,下一节 FastAPI 替你干。

以上为 UP 主的 B 站官方简介,非观看笔记;只有跟完这一节才算自己的理解。

视频:【零到全栈】5.3-理解HTTP,手搓一个API | 时长 45:28 | 模块 5 · 初试后端

讲义:模块 5.3:看懂 HTTP,手搓 API(李勃老师.com)

本节要点

  • API 用的对话协议就是 HTTP:浏览器开网页、Nginx 发 html、API 回 JSON,全是这套。我们之前只是没往里看。
  • 一去回两段纯文本:请求 = 请求行(方法+路径+版本) + 头 + 空行 + 体;响应 = 状态行(版本+状态码+说明) + 头 + 空行 + 体,结构对称。
  • 方法描述意图,不是数据方向:GET「给我」、POST「我提交内容请你处理」;响应永远有,与方法无关。curl 默认 GET,用了 -d 自动变 POST。
  • 状态码家族:2xx 成功、4xx 请求方的错、5xx 服务方的错。
  • 回响应哪些不能省:状态行、空行是硬要求(缺了对方直接报错),Content-Type 实践必写(省了对方只能猜格式)。
  • Content-Type 是「关于内容的说明」:内容不变、头从 text/html 改成 text/plain,浏览器就从渲染网页变成显示裸标签。
  • 服务端天然看得见:User-Agent、来源 IP、语言偏好——ipify 能报你公网 IP 的根源就在这。
  • 手搓全是重复杂活(路由/状态码/头/空行/体),任何语言写后端都是实现同一套规范——所以有了框架,下一节 FastAPI。

笔记正文(讲义整理)

HTTP 一去回

请求(去)            响应(回)
├─ 请求行            ├─ 状态行
│  方法 路径 版本      │  版本 状态码 说明
├─ 请求头(若干行)    ├─ 响应头(若干行)
├─ (空行)          ├─ (空行)
└─ 请求体(可选)     └─ 响应体(JSON 在这)

curl -v 把报文全亮出来:> 发出去的请求、< 收到的响应、* 旁白跳过。我们平时看到的 {"ip":...} 只是响应体那一行。

方法和状态码(混个脸熟)

方法意图
GET把东西给我
POST我提交内容请你处理
PUT / PATCH / DELETE整体替换 / 改一部分 / 删掉

状态码:2xx 成功,4xx 你错了,5xx 我错了。404 模块 4 是 Nginx 替我们回的,现在轮到自己回。

手搓 API:main.py

python
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
 
profile = {"heroTitle": "关于我", "heroSubtitle": "…"}
 
class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/api/profile":
            self.send_response(200)              # 状态行
            self.send_header("Content-Type", "application/json")  # 响应头
            self.end_headers()                    # 那个必须的空行
            body = json.dumps(profile, ensure_ascii=False)
            self.wfile.write(body.encode("utf-8"))  # 响应体
        else:
            self.send_response(404); self.end_headers()
 
HTTPServer(("", 8000), Handler).serve_forever()

逐行对规范:do_GET 管方法、self.path 取路径、send_response 写状态行、send_header 写头、end_headers 是空行、wfile.write 是体。

bash
python3 main.py          # 光标停住 = serve_forever 在等请求,Ctrl+C 停
curl http://localhost:8000/api/profile   # 另开终端调它
curl -v http://localhost:8000/api/profile  # 闭环:> 请求 < 响应,都是自己代码生成的

两个实验

  • 实验一(头的威力):/hello 返回 <h1>你好</h1>,Content-Type: text/html 时浏览器渲染成大标题;改成 text/plain 刷新——标签直接露出来。内容一字没变,头一变对方处理方式就变。
  • 实验二(服务端看得见):print(self.headers) 看到 curl 和浏览器自动带的 UA 等头;print(self.client_address) 看到来源 IP/端口。ipify 的谜底就在这。

HTTP 一去回:请求与响应的对称结构

和 GFG / 课程笔记的连接

这节内容GFG 笔记 / 已学
import json + json.dumpsGFG json 模块;ensure_ascii=False 让中文原样输出
class Handler(BaseHTTPRequestHandler)GFG 的类与继承
self.path / self.wfileGFG 里学过的面向对象——self 就是那个对象
http.server5.2 预告过的标准库成员,今天正式用上
端口 80002.4 讲过:IP 找机器,端口找机器上的程序
404模块 4 是 Nginx 替我们回;现在自己回

你在 GFG 里多半用 requests 当调用方调过 API;今天第一次站到被调用方——角色对调,HTTP 这层纸就捅破了。

关键概念

  • HTTP:两个程序对话的规范,API 只是用它回 JSON(而不是回 HTML)。
  • 请求行 / 状态行:请求第一行(方法+路径+版本);响应第一行(版本+状态码+说明)。
  • 请求头 / 响应头:一行一条的「附加说明」;Content-Type 说明体的格式。
  • 空行:头和体之间的硬分隔,漏了全盘皆乱。
  • 状态码:2xx 成功 / 4xx 请求方错 / 5xx 服务方错。
  • 方法:GET/POST/PUT/PATCH/DELETE,描述请求意图。
  • curl -v:把请求原文 >、响应原文 < 全打出来。

代码 / 实操

bash
# 启动自己的后端
source .venv/bin/activate
python3 main.py            # serve_forever,Ctrl+C 停
 
# 另开终端验证
curl http://localhost:8000/api/profile
curl -v http://localhost:8000/api/profile
# 浏览器开 http://localhost:8000/api/profile ,F12 → Network 看同一套报文

我的收获

  1. 「网页」和「API」从 HTTP 看没本质区别,都是一段响应,差别只在 Content-Type——做 API 就是选择回 JSON 而不是 HTML。
  2. 状态行、空行是硬要求,Content-Type 是实践必写——「规范必须」和「实践必写」的区别落到了代码上。
  3. 亲手搓一遍才知道框架替我们藏了多少活;下一节 FastAPI 缩成几行时,我知道它每一行在干什么。

待深入

(待填:没听懂、想回头查的。)