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

说实话,很多做 API 的同学,对 HTTP 的理解就停留在「GET 是查,POST 是增」这个层面。但实际工作中,你会发现 HTTP 协议远比想象中丰富。我记得刚带团队那会儿,有个同事用 POST 实现了所有接口,理由是「反正都能通」。嗯,这种用法虽然能跑,但后面维护起来,那叫一个痛苦。

今天我们就来把 HTTP 协议的几个核心点掰扯清楚。你想想看,如果连请求方法和状态码都用不对,那 API 设计得再漂亮,也只是空中楼阁。

2.1 HTTP 请求方法

HTTP 定义了一组请求方法,每个方法都有它自己的语义。说白了,就是告诉服务器「我想干什么」。我个人习惯把方法分成两类:安全方法和非安全方法。

2.1.1 GET — 查

GET 是最常用的方法。它用来获取资源,不会改变服务器状态。我在项目中遇到过有人用 GET 传大量参数,结果 URL 太长被网关截断了。所以记住:GET 请求的参数应该放在 URL 的 query string 里,而且长度有限制。

核心原则:GET 请求必须是幂等的。同一个 GET 请求发 100 次,结果应该和发 1 次一样。

2.1.2 POST — 增

POST 用来创建资源。它不像 GET 那样「轻量」,因为每次 POST 都会在服务器上产生一个新资源。我曾经犯过一个错:用 POST 做查询操作,结果缓存策略全乱套了。后来才明白,POST 天生不适合做查询。

避坑指南:POST 不是幂等的。你发两次 POST,服务器上会多出两个资源。这一点和 GET 完全不同。

2.1.3 PUT — 全量更新

PUT 用来替换整个资源。你传什么,服务器就存什么。如果某个字段没传,那就相当于把这个字段置空。我记得有个项目,前端用 PUT 更新用户信息,漏传了手机号字段,结果用户手机号全被清空了。嗯,这个坑我踩过。

小技巧:PUT 是幂等的。同一个 PUT 请求发多次,结果都一样。这一点可以用来做重试机制。

2.1.4 DELETE — 删

DELETE 用来删除资源。它也是幂等的。你删一次和删十次,结果一样——资源都不存在了。我在项目中遇到过 DELETE 返回 200 还是 204 的争论。我个人习惯:删除成功返回 204 No Content,不返回 body。

2.1.5 PATCH — 部分更新

PATCH 和 PUT 的区别,说白了就是「部分更新」vs「全量替换」。PATCH 只更新你传的字段,其他字段保持不变。这个在微服务架构里特别有用。我曾经用 PATCH 实现了一个「只更新用户头像」的接口,前端传一个字段就够了,清爽得很。

方法 语义 幂等 请求体
GET 获取资源
POST 创建资源
PUT 全量更新
DELETE 删除资源 可选
PATCH 部分更新

2.2 HTTP 状态码

状态码是服务器对客户端说「人话」的方式。你想想看,如果所有请求都返回 200,那出了问题你根本不知道是哪里挂了。我见过最离谱的项目,所有接口统一返回 200,错误信息写在 body 里。这种设计,说白了就是给自己挖坑。

2.2.1 2xx — 成功

  • 200 OK:最常用的成功码。GET 请求成功返回 200。
  • 201 Created:POST 创建资源成功时用。记得在响应头里带上 Location 字段,指向新资源的 URL。
  • 204 No Content:DELETE 成功时用。没有 body,干净利落。

个人经验:我习惯把 201 和 Location 头配合使用。这样客户端拿到 201 后,直接就能知道新资源的地址,不用再去猜。

2.2.2 3xx — 重定向

3xx 状态码表示「你要的东西不在这儿,去别处找」。最常见的两个:

  • 301 Moved Permanently:永久重定向。浏览器会缓存这个结果。
  • 302 Found:临时重定向。每次都要重新请求。

我在项目中遇到过 301 和 302 用混的情况。结果客户端缓存了错误的地址,导致功能异常。嗯,这里要注意:API 版本升级时,用 301 还是 302,得想清楚。

2.2.3 4xx — 客户端错误

4xx 状态码表示「你发的东西有问题」。这是 API 设计中最常用的一类状态码。

  • 400 Bad Request:请求格式不对。比如 JSON 解析失败。
  • 401 Unauthorized:没登录。或者 token 过期了。
  • 403 Forbidden:登录了,但没权限。这个和 401 的区别要搞清楚。
  • 404 Not Found:资源不存在。最常见的错误码。
  • 405 Method Not Allowed:方法不支持。比如对只读接口发了 POST。
  • 409 Conflict:资源冲突。比如重复创建。
  • 422 Unprocessable Entity:语义错误。比如字段校验不通过。

避坑指南:我曾经见过一个项目,所有参数校验失败都返回 400。结果前端根本分不清是「参数缺失」还是「参数格式错误」。后来我建议他们:参数缺失用 400,参数格式错误用 422。这样前端处理起来就清晰多了。

2.2.4 5xx — 服务端错误

5xx 状态码表示「服务器出问题了」。这类错误应该尽量少出现。

  • 500 Internal Server Error:服务器内部错误。最通用的错误码。
  • 502 Bad Gateway:网关错误。通常是上游服务挂了。
  • 503 Service Unavailable:服务暂时不可用。比如过载了。
  • 504 Gateway Timeout:网关超时。上游服务响应太慢。

小技巧:5xx 错误一定要记录日志。我习惯在返回 500 的同时,把错误 ID 放在响应头里。这样排查问题时,直接搜错误 ID 就能找到对应的日志。

2.3 HTTP 头部与缓存控制

HTTP 头部是客户端和服务器之间传递「元数据」的方式。说白了,就是告诉对方「我这个请求/响应有什么特殊之处」。我个人觉得,头部设计得好,能省掉很多不必要的沟通成本。

2.3.1 常见请求头

  • Content-Type:告诉服务器 body 的格式。比如 application/json
  • Authorization:携带认证信息。比如 Bearer token。
  • Accept:告诉服务器客户端能接受什么格式。比如 application/json
  • User-Agent:标识客户端类型。可以用来做统计分析。

2.3.2 常见响应头

  • Content-Type:告诉客户端 body 的格式。
  • Cache-Control:缓存策略。这个后面会详细讲。
  • ETag:资源的唯一标识。用于条件请求。
  • Location:重定向目标地址。或者新创建资源的地址。

2.3.3 缓存控制

缓存控制是 API 性能优化的关键。你想想看,如果每次请求都打到数据库,那服务器迟早要崩。我见过一个项目,没有做任何缓存控制,结果高峰期数据库连接数直接爆了。

常用的缓存控制策略:

  • Cache-Control: max-age=3600:告诉客户端这个响应可以缓存 1 小时。
  • Cache-Control: no-cache:可以缓存,但每次使用前要问服务器是否过期。
  • Cache-Control: no-store:禁止缓存。敏感数据用这个。
  • ETag:配合 If-None-Match 使用。如果资源没变,返回 304 Not Modified。

个人经验:我习惯对 GET 接口设置 Cache-Control: max-age=60,对 POST/PUT/DELETE 设置 Cache-Control: no-store。这样既保证了性能,又避免了数据不一致的问题。

小技巧:ETag 可以用资源的哈希值。比如对用户信息做 MD5,内容变了 ETag 就变。这样客户端可以省掉很多不必要的请求。

好了,HTTP 协议基础就讲到这里。下一章我们会聊聊 RESTful API 的设计原则。到时候你会发现,今天讲的这些内容,都是后面设计规范的基础。嗯,先把地基打牢,后面才能盖高楼。