HTTP协议与实战
💡 核心结论
HTTP是无状态协议,通过Cookie/Session实现状态管理
HTTP/1.1支持持久连接,减少TCP握手开销
HTTPS = HTTP + TLS,通过非对称加密交换对称密钥
HTTP/2多路复用解决队头阻塞,HTTP/3基于QUIC进一步优化
RESTful API设计应遵循资源导向和统一接口原则
1. HTTP基础
1.1 HTTP请求格式
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml
Accept-Language: zh-CN,zh;q=0.9
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: session_id=abc123
请求行:
方法 路径 版本
GET /api/users HTTP/1.1
请求头:
Host: 目标主机(HTTP/1.1必需)User-Agent: 客户端信息Accept: 可接受的内容类型Cookie: 状态信息
请求体(POST/PUT):
POST /api/users HTTP/1.1
Content-Type: application/json
Content-Length: 45
{"name":"张三","age":25,"email":"test@qq.com"}
1.2 HTTP响应格式
HTTP/1.1 200 OK
Date: Mon, 23 Oct 2023 12:00:00 GMT
Server: nginx/1.18.0
Content-Type: text/html; charset=UTF-8
Content-Length: 1234
Connection: keep-alive
Set-Cookie: session_id=xyz789; HttpOnly
Cache-Control: max-age=3600
<!DOCTYPE html>
<html>
...
</html>
状态行:
版本 状态码 原因短语
HTTP/1.1 200 OK
响应头:
Server: 服务器信息Content-Type: 内容类型Content-Length: 内容长度Set-Cookie: 设置Cookie
2. HTTP方法
2.1 常用方法
方法 |
说明 |
幂等性 |
安全性 |
|---|---|---|---|
GET |
获取资源 |
✅ |
✅ |
POST |
创建资源 |
❌ |
❌ |
PUT |
更新资源(完整) |
✅ |
❌ |
PATCH |
更新资源(部分) |
❌ |
❌ |
DELETE |
删除资源 |
✅ |
❌ |
HEAD |
获取头部 |
✅ |
✅ |
OPTIONS |
获取支持的方法 |
✅ |
✅ |
幂等性:多次执行结果相同 安全性:不修改服务器状态
2.2 GET vs POST
特性 |
GET |
POST |
|---|---|---|
参数位置 |
URL query string |
请求体 |
参数长度 |
有限制(~2KB) |
无限制 |
缓存 |
可缓存 |
不可缓存 |
历史记录 |
保留在浏览器历史 |
不保留 |
书签 |
可收藏 |
不可收藏 |
安全性 |
参数暴露在URL |
相对安全 |
用途 |
获取数据 |
提交数据 |
2.3 RESTful API设计
# 获取所有用户
GET /api/users
# 获取单个用户
GET /api/users/123
# 创建用户
POST /api/users
Body: {"name":"张三","age":25}
# 更新用户(完整)
PUT /api/users/123
Body: {"name":"李四","age":26,"email":"li@qq.com"}
# 更新用户(部分)
PATCH /api/users/123
Body: {"age":27}
# 删除用户
DELETE /api/users/123
# 获取用户的订单
GET /api/users/123/orders
# 分页和过滤
GET /api/users?page=2&size=20&role=admin
3. HTTP状态码
3.1 状态码分类
1xx 信息:
100 Continue: 继续请求101 Switching Protocols: 切换协议(WebSocket)
2xx 成功:
200 OK: 成功201 Created: 已创建204 No Content: 无内容
3xx 重定向:
301 Moved Permanently: 永久重定向302 Found: 临时重定向304 Not Modified: 未修改(缓存)
4xx 客户端错误:
400 Bad Request: 请求错误401 Unauthorized: 未认证403 Forbidden: 禁止访问404 Not Found: 未找到429 Too Many Requests: 请求过多
5xx 服务器错误:
500 Internal Server Error: 服务器错误502 Bad Gateway: 网关错误503 Service Unavailable: 服务不可用504 Gateway Timeout: 网关超时
3.2 常见场景
# 成功创建资源
HTTP/1.1 201 Created
Location: /api/users/123
# 无权限访问
HTTP/1.1 403 Forbidden
{"error":"Permission denied"}
# 资源不存在
HTTP/1.1 404 Not Found
{"error":"User not found"}
# 参数验证失败
HTTP/1.1 400 Bad Request
{"error":"Invalid email format"}
# 服务器内部错误
HTTP/1.1 500 Internal Server Error
{"error":"Database connection failed"}
4. HTTP头部详解
4.1 请求头
# 缓存控制
Cache-Control: no-cache, no-store, must-revalidate
If-None-Match: "abc123" # ETag
If-Modified-Since: Mon, 23 Oct 2023 12:00:00 GMT
# 内容协商
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,en;q=0.9
Accept-Encoding: gzip, deflate, br
# 认证
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# 跨域
Origin: https://example.com
Referer: https://example.com/page
# 连接控制
Connection: keep-alive
Keep-Alive: timeout=5, max=1000
# 代理
X-Forwarded-For: 192.168.1.100
X-Real-IP: 203.0.113.5
4.2 响应头
# 缓存控制
Cache-Control: public, max-age=31536000
ETag: "abc123"
Last-Modified: Mon, 23 Oct 2023 12:00:00 GMT
Expires: Tue, 24 Oct 2023 12:00:00 GMT
# 安全
Strict-Transport-Security: max-age=31536000
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'
# CORS
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
# 内容
Content-Type: application/json; charset=utf-8
Content-Length: 1234
Content-Encoding: gzip
Transfer-Encoding: chunked
6. HTTPS
6.1 HTTPS握手过程
1. ClientHello
客户端 → 服务器
- 支持的TLS版本
- 支持的加密套件
- 随机数(Client Random)
2. ServerHello
服务器 → 客户端
- 选择的TLS版本
- 选择的加密套件
- 随机数(Server Random)
- 服务器证书(含公钥)
3. 客户端验证证书
- 检查证书有效期
- 检查证书链
- 检查域名是否匹配
4. 客户端生成Pre-Master Secret
- 用服务器公钥加密
- 发送给服务器
5. 双方生成会话密钥
- Master Secret = f(Client Random, Server Random, Pre-Master Secret)
- 用于后续对称加密
6. 握手完成
- 双方发送Finished消息
- 开始加密通信
6.2 对称加密 vs 非对称加密
特性 |
对称加密 |
非对称加密 |
|---|---|---|
密钥 |
同一个 |
公钥+私钥 |
速度 |
快 |
慢 |
安全性 |
密钥交换困难 |
安全 |
算法 |
AES, DES |
RSA, ECC |
用途 |
数据加密 |
密钥交换、签名 |
HTTPS结合方案:
非对称加密(慢)→ 交换对称密钥
对称加密(快)→ 加密实际数据
6.3 证书验证
# 查看证书信息
openssl s_client -connect www.baidu.com:443 -showcerts
# 证书链
Root CA (根证书)
↓
Intermediate CA (中间证书)
↓
Server Certificate (服务器证书)
# 验证证书
openssl verify -CAfile ca.crt server.crt
7. HTTP版本演进
7.1 HTTP/1.0
每个请求一个TCP连接
请求 → 响应 → 关闭连接 → 新请求 → 新连接
7.2 HTTP/1.1
持久连接(Keep-Alive)
一个TCP连接上多个请求
请求1 → 响应1 → 请求2 → 响应2 ...
问题:队头阻塞
请求1(慢) → 请求2(快,但必须等待)
7.3 HTTP/2
特性:
多路复用:一个连接上并行传输多个请求
头部压缩:HPACK算法压缩头部
服务器推送:主动推送资源
二进制帧:不再是文本协议
HTTP/1.1:
请求1 ━━━━━━━━━━━━━━━━━━━━ 响应1
请求2 ━━━━━━━━━━━━━━━━━━━━ 响应2
HTTP/2:
请求1 ━━━━━━━━ 响应1
请求2 ━━━━ 响应2
请求3 ━━━━━━ 响应3
(交错传输)
7.4 HTTP/3
基于QUIC(UDP):
0-RTT连接建立
改进的拥塞控制
连接迁移(IP变化不断连接)
无队头阻塞
8. 实战示例
8.1 Python HTTP客户端
import requests
# GET请求
response = requests.get('https://api.github.com/users/octocat')
print(response.status_code) # 200
print(response.json())
# POST请求
data = {'name': '张三', 'age': 25}
response = requests.post('https://api.example.com/users', json=data)
# 自定义头部
headers = {
'Authorization': 'Bearer token123',
'User-Agent': 'My App/1.0'
}
response = requests.get('https://api.example.com/profile', headers=headers)
# 超时和重试
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
session = requests.Session()
retry = Retry(total=3, backoff_factor=0.3)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
response = session.get('https://api.example.com/data', timeout=5)
8.2 Python HTTP服务器
from flask import Flask, request, jsonify
app = Flask(__name__)
users = {}
next_id = 1
# GET /api/users
@app.route('/api/users', methods=['GET'])
def get_users():
return jsonify(list(users.values()))
# GET /api/users/<id>
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
if user_id not in users:
return jsonify({'error': 'User not found'}), 404
return jsonify(users[user_id])
# POST /api/users
@app.route('/api/users', methods=['POST'])
def create_user():
global next_id
data = request.get_json()
if 'name' not in data:
return jsonify({'error': 'Name is required'}), 400
user = {'id': next_id, 'name': data['name'], 'age': data.get('age', 0)}
users[next_id] = user
next_id += 1
return jsonify(user), 201
# PUT /api/users/<id>
@app.route('/api/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
if user_id not in users:
return jsonify({'error': 'User not found'}), 404
data = request.get_json()
users[user_id].update(data)
return jsonify(users[user_id])
# DELETE /api/users/<id>
@app.route('/api/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
if user_id not in users:
return jsonify({'error': 'User not found'}), 404
del users[user_id]
return '', 204
if __name__ == '__main__':
app.run(debug=True, port=5000)
8.3 Node.js HTTP服务器
const http = require('http');
const server = http.createServer((req, res) => {
// 解析URL
const url = new URL(req.url, `http://${req.headers.host}`);
// CORS
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
// 路由
if (url.pathname === '/api/users' && req.method === 'GET') {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify([{id: 1, name: '张三'}]));
}
else if (url.pathname === '/api/users' && req.method === 'POST') {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
const user = JSON.parse(body);
res.writeHead(201, {'Content-Type': 'application/json'});
res.end(JSON.stringify({id: 2, ...user}));
});
}
else {
res.writeHead(404, {'Content-Type': 'application/json'});
res.end(JSON.stringify({error: 'Not found'}));
}
});
server.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
9. 常见问题
Q1: 为什么要用HTTPS?
A:
数据加密(防窃听)
数据完整性(防篡改)
身份验证(防冒充)
Q3: HTTP缓存如何工作?
A:
强缓存:Cache-Control, Expires
浏览器直接使用缓存,不请求服务器
协商缓存:ETag, Last-Modified
浏览器询问服务器,服务器返回304或新内容
Q4: RESTful和RPC的区别?
A:
RESTful: 资源导向,HTTP方法,无状态
RPC: 函数调用,自定义协议,可能有状态
参考资源
《HTTP权威指南》
RFC 2616 (HTTP/1.1)
RFC 7540 (HTTP/2)
MDN Web Docs - HTTP
OWASP - Web Security