强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

HTTP 协议详解教程 / 第 2 章:HTTP 基础要素

第 2 章:HTTP 基础要素

本章将系统介绍 HTTP 协议的四大基本构件:请求方法、状态码、头部字段和消息结构。这些是理解后续所有章节的基石。


2.1 HTTP 消息结构

HTTP 通信的基本单位是 消息(Message),分为 请求消息响应消息

请求消息(Request)

┌─────────────────────────────────────┐
│  请求行 (Request Line)               │  METHOD SP URI SP VERSION CRLF
├─────────────────────────────────────┤
│  请求头部 (Request Headers)          │  Header-Name: value CRLF
│  ...                                │
├─────────────────────────────────────┤
│  空行 (Blank Line)                   │  CRLF
├─────────────────────────────────────┤
│  请求体 (Request Body)              │  可选
└─────────────────────────────────────┘

实际示例:

POST /api/users HTTP/1.1\r\n
Host: api.example.com\r\n
Content-Type: application/json\r\n
Content-Length: 48\r\n
Authorization: Bearer eyJhbG...\r\n
\r\n
{"name":"Alice","email":"alice@example.com"}

响应消息(Response)

┌─────────────────────────────────────┐
│  状态行 (Status Line)               │  VERSION SP STATUS SP REASON CRLF
├─────────────────────────────────────┤
│  响应头部 (Response Headers)         │  Header-Name: value CRLF
│  ...                                │
├─────────────────────────────────────┤
│  空行 (Blank Line)                   │  CRLF
├─────────────────────────────────────┤
│  响应体 (Response Body)             │  可选
└─────────────────────────────────────┘

实际示例:

HTTP/1.1 201 Created\r\n
Content-Type: application/json\r\n
Location: /api/users/123\r\n
Content-Length: 62\r\n
\r\n
{"id":123,"name":"Alice","email":"alice@example.com"}

用 Python 手动构建 HTTP 消息

import socket

def raw_http_request():
    """手动构建 HTTP 请求,理解底层格式"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(("httpbin.org", 80))

    # 手动构建请求文本
    request = (
        "GET /get HTTP/1.1\r\n"
        "Host: httpbin.org\r\n"
        "User-Agent: raw-python/1.0\r\n"
        "Accept: application/json\r\n"
        "Connection: close\r\n"
        "\r\n"
    )

    sock.send(request.encode())
    
    # 接收响应
    response = b""
    while True:
        data = sock.recv(4096)
        if not data:
            break
        response += data

    sock.close()
    print(response.decode())

raw_http_request()

2.2 请求方法(Request Methods)

HTTP 定义了一组 请求方法,指示对资源要执行的操作。

核心方法总览

方法语义有请求体有响应体安全幂等
GET获取资源
POST提交数据/创建资源
PUT替换/创建资源
DELETE删除资源
PATCH部分更新资源
HEAD仅获取头部
OPTIONS查询支持的方法

安全(Safe):不会修改服务器上的资源 幂等(Idempotent):同一请求执行多次,效果与执行一次相同

各方法的典型用法

# GET — 获取资源
curl https://api.example.com/users/1

# POST — 创建资源
curl -X POST \
     -H "Content-Type: application/json" \
     -d '{"name":"Bob"}' \
     https://api.example.com/users

# PUT — 完整替换资源
curl -X PUT \
     -H "Content-Type: application/json" \
     -d '{"name":"Bob Updated","age":30}' \
     https://api.example.com/users/1

# PATCH — 部分更新
curl -X PATCH \
     -H "Content-Type: application/json" \
     -d '{"age":31}' \
     https://api.example.com/users/1

# DELETE — 删除资源
curl -X DELETE https://api.example.com/users/1

# HEAD — 仅获取响应头
curl -I https://api.example.com/users/1

# OPTIONS — 获取允许的方法
curl -X OPTIONS https://api.example.com/users -i

2.3 状态码(Status Codes)

状态码是服务器对请求处理结果的数字标识,由三位整数组成。

状态码分类

分类范围含义常见示例
1xx100-199信息性100 Continue, 101 Switching Protocols
2xx200-299成功200 OK, 201 Created, 204 No Content
3xx300-399重定向301 Moved Permanently, 304 Not Modified
4xx400-499客户端错误400 Bad Request, 401, 403, 404
5xx500-599服务器错误500 Internal Server Error, 502, 503

最常用的状态码

200 OK                  — 请求成功
201 Created             — 资源已创建
204 No Content          — 成功但无响应体
301 Moved Permanently   — 永久重定向
302 Found               — 临时重定向
304 Not Modified        — 未修改(缓存可用)
400 Bad Request         — 请求格式错误
401 Unauthorized        — 未认证
403 Forbidden           — 无权限
404 Not Found           — 资源不存在
405 Method Not Allowed  — 方法不允许
409 Conflict            — 冲突
429 Too Many Requests   — 请求过多
500 Internal Server Error — 服务器内部错误
502 Bad Gateway         — 网关错误
503 Service Unavailable — 服务不可用

状态码选择决策树

请求成功?
├── 是
│   ├── 创建了新资源? → 201 Created
│   ├── 无响应体返回? → 204 No Content
│   └── 其他 → 200 OK
├── 重定向?
│   ├── 永久移动? → 301
│   └── 临时移动? → 302 / 307
├── 客户端错误?
│   ├── 请求格式错误? → 400
│   ├── 未认证? → 401
│   ├── 无权限? → 403
│   ├── 资源不存在? → 404
│   └── 限流? → 429
└── 服务器错误?
    ├── 已知错误? → 500
    ├── 依赖服务故障? → 502
    └── 暂时不可用? → 503

2.4 头部字段(Header Fields)

头部字段以 名称: 值 的形式出现,每个字段占一行。

头部字段分类

类型描述常见字段
通用头请求和响应都能用Cache-Control, Connection, Date
请求头仅出现在请求中Host, User-Agent, Accept, Authorization
响应头仅出现在响应中Server, Set-Cookie, Content-Type
实体头描述消息体Content-Length, Content-Encoding

关键请求头

Host: www.example.com              # 必须,HTTP/1.1 要求
User-Agent: Mozilla/5.0 ...        # 客户端标识
Accept: text/html,application/json # 期望的响应格式
Accept-Language: zh-CN,en          # 语言偏好
Accept-Encoding: gzip, br          # 支持的压缩算法
Authorization: Bearer <token>      # 认证凭据
Content-Type: application/json     # 请求体格式
Content-Length: 128                # 请求体长度
If-None-Match: "etag-value"        # 缓存协商
Connection: keep-alive             # 连接管理

关键响应头

Content-Type: application/json; charset=utf-8
Content-Length: 256
Content-Encoding: gzip
Cache-Control: max-age=3600, public
ETag: "abc123"
Set-Cookie: session=xyz; HttpOnly; Secure
Location: /new-resource            # 用于重定向
Server: nginx/1.24
Access-Control-Allow-Origin: *     # CORS
Strict-Transport-Security: max-age=31536000  # HSTS

2.5 业务场景:用户注册流程

以下是一个用户注册 API 的完整 HTTP 交互过程:

请求

POST /api/v1/users HTTP/1.1
Host: api.myapp.com
Content-Type: application/json
Accept: application/json
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
Content-Length: 82

{
    "username": "alice",
    "email": "alice@example.com",
    "password": "P@ssw0rd123"
}

成功响应(201)

HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/v1/users/123
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
Content-Length: 98

{
    "id": 123,
    "username": "alice",
    "email": "alice@example.com",
    "created_at": "2026-05-10T08:00:00Z"
}

错误响应(400)

HTTP/1.1 400 Bad Request
Content-Type: application/json
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
Content-Length: 112

{
    "error": {
        "code": "VALIDATION_ERROR",
        "message": "邮箱格式不正确",
        "details": [
            {"field": "email", "message": "必须是有效的邮箱地址"}
        ]
    }
}

Node.js 服务端实现

const http = require('http');

const server = http.createServer((req, res) => {
    if (req.method === 'POST' && req.url === '/api/v1/users') {
        let body = '';

        req.on('data', chunk => body += chunk);
        req.on('end', () => {
            try {
                const data = JSON.parse(body);
                
                // 简单验证
                if (!data.email || !data.email.includes('@')) {
                    res.writeHead(400, {'Content-Type': 'application/json'});
                    res.end(JSON.stringify({
                        error: {
                            code: 'VALIDATION_ERROR',
                            message: '邮箱格式不正确'
                        }
                    }));
                    return;
                }

                // 创建用户
                res.writeHead(201, {
                    'Content-Type': 'application/json',
                    'Location': '/api/v1/users/123'
                });
                res.end(JSON.stringify({
                    id: 123,
                    username: data.username,
                    email: data.email,
                    created_at: new Date().toISOString()
                }));
            } catch (e) {
                res.writeHead(400, {'Content-Type': 'application/json'});
                res.end(JSON.stringify({error: {message: '请求体不是有效 JSON'}}));
            }
        });
    } else {
        res.writeHead(404, {'Content-Type': 'application/json'});
        res.end(JSON.stringify({error: {message: '路由不存在'}}));
    }
});

server.listen(3000, () => console.log('Server running on :3000'));

2.6 请求-响应生命周期

客户端                          服务器
  │                               │
  │ ─── DNS 解析 ────────────────→│
  │ ←── IP 地址 ───────────────── │
  │                               │
  │ ─── TCP 三次握手 ────────────→│
  │ ←── 连接建立 ──────────────── │
  │                               │
  │ ─── TLS 握手 (HTTPS) ───────→│
  │ ←── 证书 & 密钥交换 ──────── │
  │                               │
  │ ─── HTTP 请求 ──────────────→│
  │                               │──→ 路由匹配
  │                               │──→ 权限验证
  │                               │──→ 业务逻辑
  │                               │──→ 生成响应
  │ ←── HTTP 响应 ─────────────── │
  │                               │
  │ ─── 关闭连接 / 复用 ────────→│

2.7 Content-Type 常见值

Content-Type 指定请求体或响应体的媒体类型:

Content-Type说明示例场景
text/htmlHTML 文档网页响应
text/plain纯文本简单文本响应
application/jsonJSON 数据RESTful API
application/xmlXML 数据传统 API
application/x-www-form-urlencoded表单数据HTML 表单提交
multipart/form-data多部分表单文件上传
application/octet-stream二进制流文件下载
image/jpegJPEG 图片图片资源
# JSON 格式
curl -X POST \
     -H "Content-Type: application/json" \
     -d '{"name":"Alice"}' \
     http://localhost:3000/api

# 表单格式
curl -X POST \
     -d "name=Alice&age=25" \
     http://localhost:3000/form

# 文件上传
curl -X POST \
     -F "file=@/path/to/image.jpg" \
     -F "description=my photo" \
     http://localhost:3000/upload

⚠️ 注意事项

  1. Host 头必须存在:HTTP/1.1 强制要求,否则返回 400
  2. 大小写敏感性:头部字段名不区分大小写,但值可能区分
  3. CRLF 换行:HTTP 规范要求 \r\n 作为行终止符,不是 \n
  4. 不要过度依赖 GET 获取敏感数据:GET 的 URL 会被记录在日志、浏览器历史中
  5. POST 不是万能的:选择正确的方法(PUT 替换、PATCH 更新、DELETE 删除)有助于 API 清晰度

🔗 扩展阅读


下一章第 3 章:URL 与 URI — URL 语法、编码规则、查询参数、片段标识