2、HTTP协议基础:HTTP请求方法、状态码、请求头与响应头

说实话,HTTP协议这东西,你天天在用,但很多人其实没真正搞懂。

我记得刚入行那会儿,写API接口就知道GET和POST。后来线上出了个事故——有人用GET请求删了用户数据。嗯,那次教训挺深刻的。从那以后,我对HTTP方法的理解才算真正到位了。

2.1 HTTP请求方法:不只是GET和POST

HTTP定义了多种请求方法,每种都有它的语义和用途。我个人习惯把它们分成两类:安全方法非安全方法

GET:获取资源

GET是最常用的方法。它的特点是幂等安全——说白了,你调多少次,结果都一样,而且不会改变服务器状态。

关键点:GET请求不应该有请求体(body)。参数通过URL传递。

# 好的GET请求
GET /api/users?page=1&size=20 HTTP/1.1

# 坏的GET请求(带请求体)
GET /api/users HTTP/1.1
Content-Type: application/json

{"page": 1, "size": 20}  # 不要这样做!

我曾经踩过的坑:有个同事把删除操作做成了GET请求。结果搜索引擎爬虫一爬,数据全没了。记住:任何修改数据的操作,都不要用GET

POST:创建资源

POST用于创建新资源。它不幂等——你发两次同样的POST,可能会创建两个资源。

POST /api/users HTTP/1.1
Content-Type: application/json

{
  "name": "张三",
  "email": "zhangsan@example.com"
}

PUT:全量更新

PUT用于替换整个资源。它是幂等的——你发多少次,最终结果都一样。

我的经验:PUT要求客户端提供完整的资源数据。如果只更新部分字段,用PATCH更合适。

PUT /api/users/123 HTTP/1.1
Content-Type: application/json

{
  "name": "李四",
  "email": "lisi@example.com",
  "age": 30
}

PATCH:部分更新

PATCH用于部分更新资源。它不一定是幂等的——取决于你的实现方式。

PATCH /api/users/123 HTTP/1.1
Content-Type: application/json

{
  "email": "newemail@example.com"
}

注意:PATCH的请求体格式有讲究。我建议使用JSON Patch(RFC 6902)或JSON Merge Patch(RFC 7396)标准。

DELETE:删除资源

DELETE用于删除资源。它是幂等的——第一次删除返回200,第二次可能返回404,但服务器状态是一样的。

DELETE /api/users/123 HTTP/1.1

2.2 HTTP状态码:服务器的“表情包”

状态码是服务器对请求的回应。我把它比作服务器的“表情包”——一看就知道发生了什么。

2xx:成功

状态码 含义 使用场景
200 OK 请求成功 GET、PUT、PATCH成功
201 Created 资源创建成功 POST创建资源后
204 No Content 请求成功,无返回内容 DELETE成功

我的习惯:POST创建资源后,返回201 + Location头(指向新资源的URL)。DELETE成功后,返回204,不返回body。

3xx:重定向

3xx告诉客户端,你需要去别的地方找资源。

状态码 含义 说明
301 Moved Permanently 永久重定向 搜索引擎会更新URL
302 Found 临时重定向 搜索引擎保留原URL
304 Not Modified 资源未修改 配合缓存使用

我曾经遇到的坑:API版本升级时用了302,结果客户端缓存了旧URL。后来改用301,配合Location头,问题解决了。

4xx:客户端错误

4xx表示客户端有问题——你发错了请求。

状态码 含义 常见原因
400 Bad Request 请求格式错误 JSON解析失败、参数缺失
401 Unauthorized 未认证 缺少Token或Token过期
403 Forbidden 无权限 认证了但没权限
404 Not Found 资源不存在 URL写错了
409 Conflict 资源冲突 版本冲突、唯一约束
422 Unprocessable Entity 语义错误 校验失败

关键原则:4xx错误一定要在响应体里给出详细的错误信息。我习惯用统一的错误格式:

{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "用户ID 123 不存在",
    "details": "请检查用户ID是否正确"
  }
}

5xx:服务器错误

5xx表示服务器出问题了——不是你的错。

状态码 含义 说明
500 Internal Server Error 服务器内部错误 未捕获的异常
502 Bad Gateway 网关错误 上游服务挂了
503 Service Unavailable 服务不可用 过载、维护中
504 Gateway Timeout 网关超时 上游响应太慢

我的建议:5xx错误不要暴露堆栈信息给客户端。记录日志就好,返回一个通用的错误消息。

2.3 请求头与响应头:HTTP的“元数据”

HTTP头是请求和响应的“元数据”。你想想看,没有头信息,服务器怎么知道客户端能接受什么格式的数据?

常见的请求头

请求头 作用 示例
Content-Type 请求体的格式 application/json
Accept 客户端能接受的响应格式 application/json
Authorization 认证信息 Bearer <token>
User-Agent 客户端标识 Mozilla/5.0...

常见的响应头

响应头 作用 示例
Content-Type 响应体的格式 application/json
Location 新资源的URL /api/users/123
Cache-Control 缓存策略 max-age=3600
ETag 资源版本标识 "33a64df5"

我的经验:API设计时,一定要明确Content-Type。我见过太多因为忘记设置Content-Type导致解析失败的案例。

2.4 实战:用Python实现一个简单的HTTP客户端

光说不练假把式。我们来看看Python里怎么用requests库发送各种HTTP请求。

import requests

# GET请求
response = requests.get('https://api.example.com/users', params={'page': 1})
print(response.status_code)  # 200
print(response.json())       # 解析JSON响应

# POST请求
response = requests.post(
    'https://api.example.com/users',
    json={'name': '张三', 'email': 'zhangsan@example.com'},
    headers={'Authorization': 'Bearer your-token'}
)
print(response.status_code)  # 201

# PUT请求
response = requests.put(
    'https://api.example.com/users/123',
    json={'name': '李四', 'email': 'lisi@example.com', 'age': 30}
)

# PATCH请求
response = requests.patch(
    'https://api.example.com/users/123',
    json={'email': 'newemail@example.com'}
)

# DELETE请求
response = requests.delete('https://api.example.com/users/123')
print(response.status_code)  # 204

总结一下:

  • GET用于获取,POST用于创建
  • PUT全量更新,PATCH部分更新
  • DELETE用于删除
  • 2xx成功,3xx重定向,4xx客户端错,5xx服务器错
  • 请求头和响应头是HTTP的元数据,别忽略它们

嗯,这一章的内容就到这里。下一章我们会深入聊聊RESTful API的设计原则——那才是真正有意思的部分。