HTTP 协议核心:请求与响应结构、常见方法、状态码详解
说实话,HTTP 协议这东西,你天天在用,但真要把它说清楚,很多人反而含糊了。我面试过不少自称“熟悉 HTTP”的候选人,一问到请求报文长什么样、状态码到底怎么分类,就开始支支吾吾。
这一章,咱们就把 HTTP 协议的核心扒开来看。不扯虚的,全是你在实际开发中每天都要打交道的东西。
HTTP 请求报文长什么样?
先看一个最普通的 GET 请求。你打开浏览器,输入网址,敲回车,浏览器就帮你组装了这么一段东西:
GET /api/users HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: application/json
嗯,就这么简单。我把它拆成三部分:
- 请求行:第一行。包含方法(GET)、路径(/api/users)、协议版本(HTTP/1.1)。
- 请求头:从第二行开始,到空行结束。携带元数据,比如 Host、Cookie、Content-Type。
- 请求体:空行之后的内容。GET 请求没有体,POST 请求才有。
你想想看,这结构是不是很清晰?每一行都有自己的职责,谁也不越界。
关键点:请求头和请求体之间必须有一个空行。这是协议规定的分隔符。我曾经见过一个新手,在拼接请求报文时忘了加这个空行,结果服务端一直收不到 body,排查了半天。
POST 请求的报文长什么样?
POST 请求和 GET 最大的区别,就是它有请求体。比如你提交一个表单:
POST /api/login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
username=admin&password=123456
注意看,请求头里多了 Content-Type 和 Content-Length。这两个字段告诉服务端:body 是什么格式,有多长。服务端拿到后,按这个格式去解析 body。
我个人习惯,在写后端接口时,一定会校验 Content-Type。如果客户端传的是 JSON,但 Content-Type 写成了 form-urlencoded,我会直接返回 400。这不是矫情,是安全规范。
HTTP 响应报文长什么样?
服务端处理完请求,会返回一个响应。结构跟请求几乎一样,只是第一行变成了状态行:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 42
{"status":"success","data":[]}
状态行包含三部分:协议版本、状态码、状态描述。200 是状态码,OK 是描述。描述其实可以随便写,但没人会乱写,因为客户端都是按状态码做逻辑判断的。
响应头里最常用的就是 Content-Type,告诉客户端怎么解析 body。body 可以是 JSON、HTML、图片二进制流,什么都行。
常见的请求方法:GET / POST / PUT / DELETE
HTTP 协议定义了多种请求方法,但日常开发中,90% 的场景只用这四个:
| 方法 | 语义 | 是否有 body | 幂等性 | 典型场景 |
|---|---|---|---|---|
| GET | 获取资源 | 否 | 是 | 查询列表、获取详情 |
| POST | 创建资源 | 是 | 否 | 注册、提交订单 |
| PUT | 更新资源(全量) | 是 | 是 | 修改用户信息 |
| DELETE | 删除资源 | 通常无 | 是 | 删除文章、注销账号 |
这里有个坑,我必须要说。很多人觉得 POST 和 PUT 差不多,都是传数据。但它们的语义完全不同。POST 是非幂等的,你调两次可能创建两条记录。PUT 是幂等的,调多少次结果都一样。我曾经接手过一个项目,所有写操作都用 POST,导致接口语义混乱,后来重构时花了不少力气。
小技巧:设计 RESTful 接口时,尽量遵循方法的原始语义。GET 只读,POST 创建,PUT 全量更新,DELETE 删除。这样你的接口自解释性会很强,别人一看就懂。
状态码详解:2xx / 3xx / 4xx / 5xx
状态码是服务端给客户端的“一句话反馈”。我把它分成四类,每一类都有明确的含义。
2xx:一切正常
- 200 OK:最常用的。请求成功,响应体里就是你要的数据。
- 201 Created:创建成功。POST 请求创建资源后,返回这个状态码,同时带上新资源的 URL。
- 204 No Content:成功,但没有响应体。DELETE 操作经常用这个。
我个人习惯,在写后端接口时,POST 创建资源一定返回 201,而不是 200。虽然 200 也能用,但 201 语义更准确,客户端可以根据状态码做不同的处理。
3xx:重定向
- 301 Moved Permanently:永久重定向。资源换了新地址,以后都走新地址。
- 302 Found:临时重定向。这次先走新地址,下次还走原地址。
- 304 Not Modified:资源没变。配合缓存使用,客户端可以继续用本地缓存。
你想想看,301 和 302 的区别是什么?301 告诉浏览器“以后别来这了”,浏览器会更新书签。302 告诉浏览器“这次先这样”,书签不变。这个区别在实际项目中很重要,尤其是做 URL 迁移时。
注意:3xx 状态码通常由浏览器自动处理。但如果你在写客户端代码(比如 axios),默认不会跟随重定向,需要手动配置 maxRedirects 或处理 Location 头。
4xx:客户端错误
- 400 Bad Request:请求格式不对。比如参数缺失、JSON 解析失败。
- 401 Unauthorized:未认证。你没登录,或者 token 过期了。
- 403 Forbidden:已认证但无权限。你登录了,但没权限访问这个资源。
- 404 Not Found:资源不存在。路径写错了,或者资源被删了。
- 405 Method Not Allowed:方法不支持。比如只允许 POST,你发了 GET。
- 429 Too Many Requests:请求太频繁。限流了,等会儿再试。
这里我特别想说一下 401 和 403 的区别。很多开发者搞混。401 是“你是谁?”,403 是“我知道你是谁,但你不配”。我在项目中见过一个接口,用户没登录时返回 403,这其实是错的,应该返回 401。正确的做法是:未登录返回 401,登录了但没权限才返回 403。
5xx:服务端错误
- 500 Internal Server Error:服务器内部错误。代码崩了,或者数据库连不上了。
- 502 Bad Gateway:网关错误。Nginx 代理的后端服务挂了。
- 503 Service Unavailable:服务暂时不可用。服务器过载,或者正在维护。
- 504 Gateway Timeout:网关超时。后端响应太慢,Nginx 等不及了。
5xx 状态码对用户来说就是“服务器炸了”。但对我们开发者来说,它是排查问题的线索。502 和 504 通常出现在网关层,500 才是应用层的问题。我记得有一次线上告警,全是 502,我第一反应是去看 Nginx 和后端服务的连通性,果然发现后端进程挂了。
知识体系结构图
下面这张图,把 HTTP 协议的核心脉络梳理了一遍。你可以把它当作本章的“地图”。
总结一下
HTTP 协议的核心,说白了就是两件事:客户端怎么问,服务端怎么答。问的格式是请求报文,答的格式是响应报文。方法决定了问的意图,状态码决定了答的结果。
你在实际开发中,每天都会跟这些东西打交道。写接口时注意方法语义,排查问题时先看状态码,调试时抓包看报文结构。把这些基本功打扎实了,后面学 HTTPS、HTTP/2、WebSocket 都会轻松很多。