1. HTTP 协议基础
目录
- [1. HTTP 协议基础](#1. HTTP 协议基础)
- [1.1 什么是 HTTP?](#1.1 什么是 HTTP?)
- [1.2 HTTP 请求结构](#1.2 HTTP 请求结构)
- [1.3 HTTP 响应结构](#1.3 HTTP 响应结构)
- [1.4 常见请求头与响应头](#1.4 常见请求头与响应头)
- 常见请求头
- 常见响应头
- Content-Type(响应)
- Content-Disposition(响应)
- Cache-Control(响应)
- Connection(响应)
- [Content-Encoding(响应,配合 Accept-Encoding)](#Content-Encoding(响应,配合 Accept-Encoding))
- [1.4.1 Content-Type 详解:常见类型与传参方式](#1.4.1 Content-Type 详解:常见类型与传参方式)
- 格式说明
- 常见类型速查
- [1. application/json(最常用)](#1. application/json(最常用))
- [2. application/x-www-form-urlencoded](#2. application/x-www-form-urlencoded)
- [3. multipart/form-data(文件上传)](#3. multipart/form-data(文件上传))
- [4. text/plain](#4. text/plain)
- [5. application/xml](#5. application/xml)
- [6. application/octet-stream](#6. application/octet-stream)
- 各类型完整对比
- 常见错误与排查
- [Postman / Apifox 对照](#Postman / Apifox 对照)
- [1.5 HTTP 方法](#1.5 HTTP 方法)
- [1. GET(获取资源)](#1. GET(获取资源))
- [2. POST(创建资源)](#2. POST(创建资源))
- [3. PUT(全量更新/替换)](#3. PUT(全量更新/替换))
- [4. PATCH(部分更新)](#4. PATCH(部分更新))
- [5. DELETE(删除资源)](#5. DELETE(删除资源))
- [6. HEAD(仅获取响应头,不获取 Body)](#6. HEAD(仅获取响应头,不获取 Body))
- [7. OPTIONS(预检/探测支持的方法)](#7. OPTIONS(预检/探测支持的方法))
- [8. GET 与 POST 的区别](#8. GET 与 POST 的区别)
- [1.6 HTTP 状态码:场景举例](#1.6 HTTP 状态码:场景举例)
- 一眼速查(带举例)
- [1xx 信息性响应(⭐ 很少直接感知)](#1xx 信息性响应(⭐ 很少直接感知))
- [100 Continue ⭐](#100 Continue ⭐)
- [101 Switching Protocols ⭐](#101 Switching Protocols ⭐)
- [2xx 成功(⭐⭐⭐ 最常见)](#2xx 成功(⭐⭐⭐ 最常见))
- [200 OK ⭐⭐⭐](#200 OK ⭐⭐⭐)
- [201 Created ⭐⭐⭐](#201 Created ⭐⭐⭐)
- [204 No Content ⭐⭐](#204 No Content ⭐⭐)
- [206 Partial Content ⭐](#206 Partial Content ⭐)
- [3xx 重定向(⭐⭐ 浏览器常见,API 调用需注意)](#3xx 重定向(⭐⭐ 浏览器常见,API 调用需注意))
- [301 Moved Permanently ⭐⭐](#301 Moved Permanently ⭐⭐)
- [302 Found ⭐⭐](#302 Found ⭐⭐)
- [304 Not Modified ⭐⭐](#304 Not Modified ⭐⭐)
- [4xx 客户端错误(⭐⭐⭐ 开发中最常排查)](#4xx 客户端错误(⭐⭐⭐ 开发中最常排查))
- [400 Bad Request ⭐⭐⭐](#400 Bad Request ⭐⭐⭐)
- [401 Unauthorized ⭐⭐⭐](#401 Unauthorized ⭐⭐⭐)
- [403 Forbidden ⭐⭐⭐](#403 Forbidden ⭐⭐⭐)
- [404 Not Found ⭐⭐⭐](#404 Not Found ⭐⭐⭐)
- [405 Method Not Allowed ⭐⭐](#405 Method Not Allowed ⭐⭐)
- [408 Request Timeout ⭐](#408 Request Timeout ⭐)
- [409 Conflict ⭐⭐](#409 Conflict ⭐⭐)
- [413 Payload Too Large ⭐⭐](#413 Payload Too Large ⭐⭐)
- [415 Unsupported Media Type ⭐⭐](#415 Unsupported Media Type ⭐⭐)
- [422 Unprocessable Entity ⭐⭐](#422 Unprocessable Entity ⭐⭐)
- [429 Too Many Requests ⭐⭐](#429 Too Many Requests ⭐⭐)
- [5xx 服务器错误(⭐⭐⭐ 需要后端/运维排查)](#5xx 服务器错误(⭐⭐⭐ 需要后端/运维排查))
- [500 Internal Server Error ⭐⭐⭐](#500 Internal Server Error ⭐⭐⭐)
- [502 Bad Gateway ⭐⭐](#502 Bad Gateway ⭐⭐)
- [503 Service Unavailable ⭐⭐](#503 Service Unavailable ⭐⭐)
- [504 Gateway Timeout ⭐⭐](#504 Gateway Timeout ⭐⭐)
- [507 Insufficient Storage ⭐](#507 Insufficient Storage ⭐)
- [在 Chrome DevTools 里看状态码](#在 Chrome DevTools 里看状态码)
- [1.7 HTTP vs HTTPS](#1.7 HTTP vs HTTPS)---
1.1 什么是 HTTP?
HTTP (HyperText Transfer Protocol,超文本传输协议)是 客户端与服务器之间通信的应用层协议。它基于 请求-响应 模型,默认使用 TCP 作为传输层。
一次完整的请求流程
- 浏览器解析出请求的域名。
- 通过DNS(域名系统)获取服务器的IP地址。
- 与服务器建立TCP连接(如果是HTTPS,还需进行TLS握手)。
- 构建并发送HTTP请求报文。
- 服务器接收、解析并处理请求,然后返回HTTP响应报文。
- 浏览器解析响应,并关闭或复用TCP连接。
1.2 HTTP 请求结构
一次 HTTP 请求由三部分组成:
| 部分 | 说明 | 示例 |
|---|---|---|
| 请求行 | 方法 + URL + 协议版本 | GET /api/users HTTP/1.1 |
| 请求头 | 元数据(键值对) | Content-Type: application/json |
| 请求体 | 可选,携带数据 | {"name": "张三"} |
完整请求示例:
http
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Accept: application/json
{
"name": "张三",
"email": "zhangsan@example.com"
}
1.3 HTTP 响应结构
| 部分 | 说明 | 示例 |
|---|---|---|
| 状态行 | 协议版本 + 状态码 + 描述 | HTTP/1.1 200 OK |
| 响应头 | 元数据 | Content-Type: application/json |
| 响应体 | 实际返回的数据 | {"id": 1, "name": "张三"} |
完整响应示例:
http
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": 1,
"name": "张三",
"email": "zhangsan@example.com"
}
1.4 常见请求头与响应头
HTTP 头(Header)是键值对形式的元数据,不参与业务 Body,但决定服务端如何解析请求、客户端如何处理响应。格式统一为:
Header-Name: value
下面按请求头 与响应头分开说明日常开发中最常用的字段。
常见请求头
Content-Type
| 项目 | 说明 |
|---|---|
| 作用 | 告诉服务器:请求体(Body)是什么格式,服务端据此选择 JSON 解析器、表单解析器或文件处理器 |
| 何时需要 | POST / PUT / PATCH 等带 Body 的请求;GET 通常不需要 |
| 示例 | Content-Type: application/json |
http
POST /api/users HTTP/1.1
Content-Type: application/json
{"name": "张三", "email": "zhangsan@example.com"}
常见取值包括 application/json、application/x-www-form-urlencoded、multipart/form-data 等,各格式的 Body 写法与代码示例见 [§1.4.1 Content-Type 详解](#§1.4.1 Content-Type 详解)。
Authorization
| 项目 | 说明 |
|---|---|
| 作用 | 携带身份认证凭证,证明「我是谁」,用于访问需要登录或鉴权的接口 |
| 常见格式 | Bearer <Token>(JWT 最常见)、Basic <Base64>(用户名密码) |
| 示例 | Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... |
http
GET /api/orders HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
- Token 无效或过期时,服务器通常返回 401 Unauthorized
- 前端一般在登录成功后保存 Token,之后每次请求自动附加此头
- 注意: 不要把 Token 写在 URL 里,应放在 Header 中
User-Agent
| 项目 | 说明 |
|---|---|
| 作用 | 标识发起请求的客户端类型(浏览器、爬虫、移动端 App、Postman 等) |
| 谁发送 | 浏览器通常自动添加;后端可据此做统计、兼容性处理或反爬策略 |
| 示例 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 |
http
GET /api/products HTTP/1.1
User-Agent: MyApp/1.0 (Android 14)
- 一般无需手动设置;调用 API 时若服务端有 UA 校验,需按文档指定
- 伪造 UA 可能触发风控,生产环境应使用真实、规范的值
Accept
| 项目 | 说明 |
|---|---|
| 作用 | 告诉服务器:客户端希望收到什么格式的响应(内容协商) |
| 与 Content-Type 区别 | Accept 描述「我能接受什么响应」;请求侧的 Content-Type 描述「我发送的 Body 是什么」 |
| 示例 | Accept: application/json |
http
GET /api/users/1 HTTP/1.1
Accept: application/json
常见取值:
| Accept 值 | 含义 |
|---|---|
application/json |
期望 JSON(REST API 最常用) |
text/html |
期望 HTML 页面(浏览器访问网页) |
application/xml |
期望 XML |
*/* |
任意格式均可(默认值之一) |
可同时声明多种格式并带优先级,例如:Accept: application/json, text/plain;q=0.9(q 为权重,越大越优先)。
Cache-Control
| 项目 | 说明 |
|---|---|
| 作用 | 控制缓存行为:是否使用缓存、缓存多久、是否必须向源站验证 |
| 请求侧含义 | 客户端声明自己对缓存的要求 |
| 示例 | Cache-Control: no-cache |
http
GET /api/profile HTTP/1.1
Cache-Control: no-cache
请求侧常见指令:
| 指令 | 含义 |
|---|---|
no-cache |
可以使用缓存,但使用前必须向服务器确认是否仍有效 |
no-store |
不要存储任何缓存(敏感数据) |
max-age=0 |
立即视为过期,需重新验证 |
调试接口时,可在浏览器 DevTools 中勾选 Disable cache,等效于强制跳过本地缓存。
Connection
| 项目 | 说明 |
|---|---|
| 作用 | 控制TCP 连接是否保持(长连接 / 短连接) |
| HTTP/1.1 默认 | Connection: keep-alive(同一 TCP 连接可复用,减少握手开销) |
| 示例 | Connection: keep-alive 或 Connection: close |
http
GET /api/data HTTP/1.1
Connection: keep-alive
| 值 | 含义 |
|---|---|
keep-alive |
请求完成后不立即关闭 TCP 连接,后续请求可复用 |
close |
本次响应结束后关闭连接(HTTP/1.0 风格) |
- 空闲超时 (Idle Timeout) :这是最主要的方式。如果连接在指定时间内没有新请求,服务器就会主动关闭它。常见默认值 :Nginx 默认
75秒,Tomcat 默认30秒,Node.js 默认5分钟。 - 最大请求数限制 (Max Requests) :为防止单一连接处理过多请求,服务器会限制其最大使用次数,达到上限后关闭。常见如 Nginx 默认
100次。
Accept-Encoding
| 项目 | 说明 |
|---|---|
| 作用 | 告诉服务器:客户端支持哪些响应压缩算法,服务器可对响应 Body 压缩后再传输,节省带宽 |
| 示例 | Accept-Encoding: gzip, deflate, br |
http
GET /api/large-report HTTP/1.1
Accept-Encoding: gzip, deflate, br
| 编码 | 说明 |
|---|---|
gzip |
最普遍,几乎所有服务器和浏览器支持 |
deflate |
较少单独使用 |
br |
Brotli 压缩,现代浏览器支持,压缩率通常更好 |
浏览器会自动发送此头;服务端若返回压缩内容,会在响应头 中用 Content-Encoding: gzip 标明,客户端自动解压。
常见响应头
响应头由服务器返回,描述响应本身的属性。以下与上文请求头一一对应或配合使用。
Content-Type(响应)
| 项目 | 说明 |
|---|---|
| 作用 | 告诉客户端:响应体(Body)是什么格式,浏览器或代码据此决定如何解析 |
| 示例 | Content-Type: application/json; charset=UTF-8 |
http
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
{"id": 1, "name": "张三"}
- 返回 JSON 接口务必设置为
application/json,否则前端可能无法正确解析
Content-Disposition(响应)
| 项目 | 说明 |
|---|---|
| 作用 | 告诉浏览器如何处理响应 Body:在页面内嵌展示,还是作为附件下载;下载时可指定默认文件名 |
| 常见取值 | inline(内联展示)、attachment(触发下载) |
| 示例 | Content-Disposition: attachment; filename="report.pdf" |
http
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="report.pdf"
(PDF 二进制内容)
| 取值 / 参数 | 含义 |
|---|---|
inline |
在浏览器内直接打开(如 PDF 预览、图片显示) |
attachment |
提示用户保存为文件,弹出「另存为」或使用默认文件名下载 |
filename="..." |
建议的下载文件名(中文建议配合 filename*=UTF-8'' 编码) |
典型场景:
- 导出 Excel / PDF :
Content-Disposition: attachment; filename="订单列表.xlsx" - 在线预览 PDF :
Content-Disposition: inline+Content-Type: application/pdf - 接口返回文件流 :常与
Content-Type: application/octet-stream配合使用
http
HTTP/1.1 200 OK
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="export.xlsx"
Cache-Control(响应)
| 项目 | 说明 |
|---|---|
| 作用 | 告诉浏览器和中间代理:此响应能否缓存、缓存多久 |
| 示例 | Cache-Control: max-age=3600, public |
http
HTTP/1.1 200 OK
Cache-Control: max-age=3600, public
Content-Type: application/json
{"list": [...]}
响应侧常见指令:
| 指令 | 含义 |
|---|---|
public |
可被浏览器和 CDN 等任意缓存 |
private |
仅浏览器可缓存,CDN 不应缓存(含用户私有数据) |
max-age=3600 |
缓存 3600 秒(1 小时)内可直接使用,无需再问服务器 |
no-store |
禁止存储(登录态、支付等敏感接口) |
no-cache |
可存但必须每次向源站验证 |
静态资源(JS/CSS/图片)常设较长 max-age;动态 API 数据多用 no-cache 或 no-store。
Connection(响应)
| 项目 | 说明 |
|---|---|
| 作用 | 告知客户端本次响应后是否关闭 TCP 连接 |
| 示例 | Connection: keep-alive |
http
HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: application/json
通常与请求侧的 Connection 一致;HTTP/1.1 下多数场景为 keep-alive,由服务器或反向代理(Nginx)统一管理。
Content-Encoding(响应,配合 Accept-Encoding)
| 项目 | 说明 |
|---|---|
| 作用 | 标明响应 Body 使用的压缩方式 ,与请求头 Accept-Encoding 配合 |
| 示例 | Content-Encoding: gzip |
http
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Type: application/json
(gzip 压缩后的二进制 Body,浏览器会自动解压)
客户端在 Accept-Encoding 中声明支持 gzip 后,服务器可选择压缩响应;若未压缩,则不发送此头
请求头与响应头对照速查:
| 头名称 | 请求 | 响应 | 核心作用 |
|---|---|---|---|
Content-Type |
✅ | ✅ | 声明 Body 的媒体类型 |
Authorization |
✅ | --- | 携带认证凭证 |
User-Agent |
✅ | --- | 标识客户端类型 |
Accept |
✅ | --- | 指定期望的响应格式 |
Cache-Control |
✅ | ✅ | 控制缓存策略 |
Connection |
✅ | ✅ | 控制 TCP 连接是否保持 |
Accept-Encoding |
✅ | --- | 声明支持的压缩算法 |
Content-Encoding |
--- | ✅ | 标明响应已采用的压缩方式 |
Content-Disposition |
--- | ✅ | 控制内联展示或附件下载、文件名 |
1.4.1 Content-Type 详解:常见类型与传参方式
Content-Type 请求头声明 请求体(Body)的数据格式,服务端据此解析参数。没有 Body 的请求(如 GET)通常不需要设置此头。
格式说明
Content-Type: 主类型/子类型[; 参数=值]
示例:Content-Type: application/json; charset=UTF-8
常见类型速查
| Content-Type | 用途 | 参数位置 | 典型场景 |
|---|---|---|---|
application/json |
JSON 格式 | 请求体 Body | REST API、前后端分离 |
application/x-www-form-urlencoded |
URL 编码键值对 | 请求体 Body | 传统表单、OAuth Token |
multipart/form-data |
多部分表单 | 请求体 Body(分段) | 文件上传、含文件的表单 |
text/plain |
纯文本 | 请求体 Body | 简单文本、 Webhook |
application/xml |
XML 格式 | 请求体 Body | SOAP、旧式接口 |
application/octet-stream |
二进制流 | 请求体 Body(原始字节) | 文件直传、下载 |
text/html |
HTML 文档 | 请求体 Body | 少见(多为响应) |
#mermaid-svg-1MtEFxFrz0W7gfas{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1MtEFxFrz0W7gfas .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1MtEFxFrz0W7gfas .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1MtEFxFrz0W7gfas .error-icon{fill:#552222;}#mermaid-svg-1MtEFxFrz0W7gfas .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1MtEFxFrz0W7gfas .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1MtEFxFrz0W7gfas .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1MtEFxFrz0W7gfas .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1MtEFxFrz0W7gfas .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1MtEFxFrz0W7gfas .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1MtEFxFrz0W7gfas .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1MtEFxFrz0W7gfas .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1MtEFxFrz0W7gfas .marker.cross{stroke:#333333;}#mermaid-svg-1MtEFxFrz0W7gfas svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1MtEFxFrz0W7gfas p{margin:0;}#mermaid-svg-1MtEFxFrz0W7gfas .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1MtEFxFrz0W7gfas .cluster-label text{fill:#333;}#mermaid-svg-1MtEFxFrz0W7gfas .cluster-label span{color:#333;}#mermaid-svg-1MtEFxFrz0W7gfas .cluster-label span p{background-color:transparent;}#mermaid-svg-1MtEFxFrz0W7gfas .label text,#mermaid-svg-1MtEFxFrz0W7gfas span{fill:#333;color:#333;}#mermaid-svg-1MtEFxFrz0W7gfas .node rect,#mermaid-svg-1MtEFxFrz0W7gfas .node circle,#mermaid-svg-1MtEFxFrz0W7gfas .node ellipse,#mermaid-svg-1MtEFxFrz0W7gfas .node polygon,#mermaid-svg-1MtEFxFrz0W7gfas .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1MtEFxFrz0W7gfas .rough-node .label text,#mermaid-svg-1MtEFxFrz0W7gfas .node .label text,#mermaid-svg-1MtEFxFrz0W7gfas .image-shape .label,#mermaid-svg-1MtEFxFrz0W7gfas .icon-shape .label{text-anchor:middle;}#mermaid-svg-1MtEFxFrz0W7gfas .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1MtEFxFrz0W7gfas .rough-node .label,#mermaid-svg-1MtEFxFrz0W7gfas .node .label,#mermaid-svg-1MtEFxFrz0W7gfas .image-shape .label,#mermaid-svg-1MtEFxFrz0W7gfas .icon-shape .label{text-align:center;}#mermaid-svg-1MtEFxFrz0W7gfas .node.clickable{cursor:pointer;}#mermaid-svg-1MtEFxFrz0W7gfas .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1MtEFxFrz0W7gfas .arrowheadPath{fill:#333333;}#mermaid-svg-1MtEFxFrz0W7gfas .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1MtEFxFrz0W7gfas .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1MtEFxFrz0W7gfas .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1MtEFxFrz0W7gfas .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1MtEFxFrz0W7gfas .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1MtEFxFrz0W7gfas .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1MtEFxFrz0W7gfas .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1MtEFxFrz0W7gfas .cluster text{fill:#333;}#mermaid-svg-1MtEFxFrz0W7gfas .cluster span{color:#333;}#mermaid-svg-1MtEFxFrz0W7gfas div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1MtEFxFrz0W7gfas .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1MtEFxFrz0W7gfas rect.text{fill:none;stroke-width:0;}#mermaid-svg-1MtEFxFrz0W7gfas .icon-shape,#mermaid-svg-1MtEFxFrz0W7gfas .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1MtEFxFrz0W7gfas .icon-shape p,#mermaid-svg-1MtEFxFrz0W7gfas .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1MtEFxFrz0W7gfas .icon-shape .label rect,#mermaid-svg-1MtEFxFrz0W7gfas .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1MtEFxFrz0W7gfas .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1MtEFxFrz0W7gfas .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1MtEFxFrz0W7gfas :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
JSON 对象/数组
简单键值对
XML
原始二进制
选择 Content-Type
有文件上传?
multipart/form-data
数据结构?
application/json
application/x-www-form-urlencoded
application/xml
application/octet-stream
1. application/json(最常用)
请求头:
http
Content-Type: application/json
Body 格式: JSON 对象或数组
请求示例:
http
POST /api/users HTTP/1.1
Content-Type: application/json
{
"name": "张三",
"email": "zhangsan@example.com",
"age": 25,
"tags": ["vip", "active"]
}
JavaScript(fetch):
javascript
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: '张三',
email: 'zhangsan@example.com'
})
});
注意:
- 嵌套对象、数组直接写在 JSON 中
- 数字、布尔值不要加引号
- 中文无需额外编码(UTF-8 默认)
2. application/x-www-form-urlencoded
请求头:
http
Content-Type: application/x-www-form-urlencoded
Body 格式: key1=value1&key2=value2,特殊字符 URL 编码
| 字符 | 编码后 |
|---|---|
| 空格 | + 或 %20 |
& |
%26 |
= |
%3D |
| 中文 | %E4%B8%AD%E6%96%87 |
请求示例:
http
POST /api/login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=zhangsan&password=123456&remember=true
JavaScript:
javascript
// 方式 1:URLSearchParams(推荐)
const params = new URLSearchParams();
params.append('username', 'zhangsan');
params.append('password', '123456');
fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params.toString()
});
// 方式 2:手动拼接
const body = 'username=zhangsan&password=' + encodeURIComponent('123456');
与 JSON 对比:
| 对比项 | form-urlencoded | JSON |
|---|---|---|
| 嵌套对象 | 不支持(需 user[name] 约定) |
原生支持 |
| 数组 | 需重复 key | 原生支持 |
| 文件 | 不支持 | 不支持(需 multipart) |
| 可读性 | 简单键值对直观 | 结构复杂时更清晰 |
3. multipart/form-data(文件上传)
请求头:
http
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
boundary 是分隔各字段的随机字符串,由客户端自动生成,不要手动写固定值。
Body 格式: 多个「部分(Part)」用 boundary 分隔,每部分可含普通字段或文件
http
POST /api/upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----Boundary123
------Boundary123
Content-Disposition: form-data; name="title"
我的文档
------Boundary123
Content-Disposition: form-data; name="file"; filename="report.pdf"
<二进制文件内容>
------Boundary123--
JavaScript(FormData,无需手动设 Content-Type):
javascript
const formData = new FormData();
formData.append('title', '我的文档');
formData.append('file', fileInput.files[0]); // File 对象
fetch('/api/upload', {
method: 'POST',
body: formData
// ⚠️ 不要手动设置 Content-Type,浏览器会自动加 boundary
});
字段结构说明:
| 部分 | 说明 |
|---|---|
Content-Disposition: form-data; name="字段名" |
普通文本字段 |
Content-Disposition: form-data; name="字段名"; filename="文件名" |
文件字段 |
| 空行后的内容 | 字段值或文件二进制 |
注意:
- 同时传文件和普通字段时使用
- 使用
FormData时不要 手动设置Content-Type(boundary 由浏览器生成) - Postman 选 form-data 类型即可
4. text/plain
请求头:
http
Content-Type: text/plain
Body 格式: 纯文本字符串,无结构
http
POST /api/webhook HTTP/1.1
Content-Type: text/plain
Hello, this is a plain text message.
JavaScript:
javascript
fetch('/api/webhook', {
method: 'POST',
headers: { 'Content-Type': 'text/plain' },
body: 'Hello, this is a plain text message.'
});
5. application/xml
请求头:
http
Content-Type: application/xml
Body 格式: XML 文档
http
POST /api/soap HTTP/1.1
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<user>
<name>张三</name>
<email>zhangsan@example.com</email>
</user>
JavaScript:
javascript
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<user><name>张三</name></user>`;
fetch('/api/soap', {
method: 'POST',
headers: { 'Content-Type': 'application/xml' },
body: xml
});
6. application/octet-stream
请求头:
http
Content-Type: application/octet-stream
Body 格式: 原始二进制字节流(无结构)
javascript
// 上传原始文件字节
const buffer = await file.arrayBuffer();
fetch('/api/files', {
method: 'PUT',
headers: {
'Content-Type': 'application/octet-stream',
'Content-Disposition': `attachment; filename="${file.name}"`
},
body: buffer
});
典型场景: 大文件直传、对象存储 PUT、固件升级包上传
各类型完整对比
| 类型 | 结构 | 嵌套/数组 | 文件 | 编码 | REST API 常用 |
|---|---|---|---|---|---|
application/json |
JSON | ✅ | ❌ | UTF-8 | ⭐⭐⭐ 最常用 |
application/x-www-form-urlencoded |
键值对 | ❌ | ❌ | URL 编码 | ⭐⭐ 登录/表单 |
multipart/form-data |
多段 | ✅(多字段) | ✅ | 每段独立 | ⭐⭐ 上传 |
text/plain |
纯文本 | ❌ | ❌ | 文本 | ⭐ 少见 |
application/xml |
XML | ✅ | ❌ | UTF-8 | ⭐ SOAP 遗留 |
application/octet-stream |
二进制 | --- | ✅ | 原始字节 | ⭐ 直传 |
常见错误与排查
| 错误现象 | 原因 | 解决 |
|---|---|---|
| 415 Unsupported Media Type | 服务端不支持该 Content-Type | 对照 API 文档改用正确类型 |
| 400 + JSON 解析失败 | 发了 JSON 但 Content-Type 是 form | 改为 application/json |
| 后端收到空 Body | 用了 JSON 但 Content-Type 是 form-urlencoded | 统一类型与 Body 格式 |
| 文件上传失败 | 手动设置了 multipart 的 Content-Type 但 boundary 错误 | 用 FormData,让浏览器自动设置 |
| 中文乱码 | 缺少 charset | 加 ; charset=UTF-8 |
Postman / Apifox 对照
| 工具中的 Body 类型 | 对应 Content-Type |
|---|---|
| none | 无 Body |
| form-data | multipart/form-data |
| x-www-form-urlencoded | application/x-www-form-urlencoded |
| raw → JSON | application/json |
| raw → XML | application/xml |
| raw → Text | text/plain |
| binary | application/octet-stream |
1.5 HTTP 方法
| 方法 | 语义 | 是否幂等 | 是否有请求体 |
|---|---|---|---|
| GET | 获取资源 | 是 | 否 |
| POST | 创建资源 | 否 | 是 |
| PUT | 全量替换资源 | 是 | 是 |
| PATCH | 部分更新资源 | 否 | 是 |
| DELETE | 删除资源 | 是 | 否 |
| HEAD | 同 GET,但只返回响应头 | 是 | 否 |
| OPTIONS | 查询服务器支持的方法(含浏览器预检) | 是 | 否 |
幂等:多次相同请求产生的效果与一次相同。GET、PUT、DELETE 通常是幂等的;POST 不是。
1 . GET(获取资源)
-
前端 Fetch
// 路径参数
fetch('/api/users/123');
// 查询参数(手动拼URL)
fetch('/api/users?page=1&size=10'); -
后端 Spring Boot
@GetMapping("/users/{id}")
public User get(@PathVariable Long id) { return service.get(id); }
@GetMapping("/users")
public Listlist(@RequestParam int page, @RequestParam int size) {
return service.list(page, size);
}
2. POST(创建资源)
-
前端 Fetch
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '张三', age: 25 })
}); -
后端 Spring Boot
@PostMapping("/users")
public User create(@RequestBody UserDto dto) {
return service.save(dto);
}
3. PUT(全量更新/替换)
-
前端 Fetch
fetch('/api/users/123', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '张三新名', age: 30, email: 'new@xx.com' }) // 必须传全量字段
}); -
后端 Spring Boot
@PutMapping("/users/{id}")
public User update(@PathVariable Long id, @RequestBody UserDto dto) {
return service.fullUpdate(id, dto); // 全量覆盖
}
4. PATCH(部分更新)
-
前端 Fetch
fetch('/api/users/123', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'new_email@xx.com' }) // 只传要改的字段
}); -
后端 Spring Boot
@PatchMapping("/users/{id}")
public User patch(@PathVariable Long id, @RequestBody Map<String, Object> patch) {
return service.partialUpdate(id, patch); // 只更新传入的非空字段
}
5. DELETE(删除资源)
-
前端 Fetch (参数放 URL,禁止放 Body)
// 删除单个
fetch('/api/users/123', { method: 'DELETE' });
// 批量删除(Query传参)
fetch('/api/users?ids=1&ids=2&ids=3', { method: 'DELETE' }); -
后端 Spring Boot
@DeleteMapping("/users/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build(); // 返回 204
}
@DeleteMapping("/users")
public ResponseEntity<Void> deleteBatch(@RequestParam List<Long> ids) {
service.deleteBatch(ids);
return ResponseEntity.noContent().build();
}
6. HEAD(仅获取响应头,不获取 Body)
-
前端 Fetch
fetch('/api/files/123', { method: 'HEAD' }).then(res => {
console.log(res.headers.get('Content-Length')); // 获取文件大小
console.log(res.headers.get('Last-Modified')); // 获取修改时间
// 注意:此时 res.body 为空,无法读取数据
}); -
后端 Spring Boot
// 方式一:@GetMapping 默认自动支持 HEAD(但会执行完整查询逻辑,浪费性能)
// 方式二:专门写轻量级 HEAD 逻辑(推荐)
@RequestMapping(value = "/files/{id}", method = RequestMethod.HEAD)
public ResponseEntityhead(@PathVariable Long id) {
long size = service.getFileSize(id); // 只查元数据,不读文件流
return ResponseEntity.ok()
.header("Content-Length", String.valueOf(size))
.build();
}
7. OPTIONS(预检/探测支持的方法)
-
前端 Fetch(通常浏览器自动发,无需手动,但可测试)
fetch('/api/users', { method: 'OPTIONS' }).then(res => {
console.log(res.headers.get('Allow')); // 打印服务器支持的方法,如 GET,POST,PUT,DELETE
}); -
后端 Spring Boot (无需编写任何代码)
java
// Spring 框架自动响应 OPTIONS 请求。
// 只要在类上或方法上标注了 @CrossOrigin 或全局 CORS 配置,
// 框架会自动返回 Allow 头部和跨域相关头部。
// 如果完全没配 CORS,OPTIONS 也会返回默认支持的 Allow 头。
这是浏览器强制自动 发出的,只有同时满足以下两个条件才会触发:
-
条件一(跨域) :前端页面域名(如
localhost:3000)和后端接口域名(如api.example.com)不同。 -
条件二(非简单请求):请求不满足"简单请求"标准。最常见的是:
-
用了
PUT、DELETE、PATCH方法。 -
请求头带了
Authorization或X-Token等自定义头。 -
Content-Type是application/json。注意 :如果是跨域但满足"简单请求" (比如纯
GET请求,且不带自定义头,只传text/plain),浏览器不会 发OPTIONS,直接发GET。
-
8、 GET 与 POST 的区别
GET 和 POST 是最常用的两种方法,初学者容易混淆。核心差异如下:
| 对比维度 | GET | POST |
|---|---|---|
| 语义 | 获取/读取资源 | 创建资源或提交数据 |
| 参数位置 | URL 查询字符串(Query String) | 请求体(Body) |
| 是否有 Body | 否(规范上不携带 Body) | 是(JSON、表单等) |
| 是否幂等 | 是(重复请求不改变资源) | 否(重复提交可能创建多条记录) |
| 是否可缓存 | 通常可缓存 | 通常不缓存 |
| 参数长度 | 受 URL 长度限制(约 2KB~8KB,因浏览器/服务器而异) | 无严格限制,适合大数据量 |
| 可见性 | 参数出现在 URL 中,会进入浏览器历史、服务器访问日志 | 参数在 Body 中,不显示在地址栏 |
| 典型场景 | 查询列表、搜索、分页、获取详情 | 登录、注册、创建订单、提交表单、文件上传 |
| 如何选用: |
- 读数据、不改变服务器状态 → 用 GET (如
/api/products?category=phone) - 写数据、创建或提交 → 用 POST (如
POST /api/orders创建订单) - 敏感信息 (密码、Token)→ 应放在 POST Body 中,不要放在 GET 的 URL 参数里
常见误区: 「POST 比 GET 安全」并不绝对------POST 只是参数不出现在 URL 中;传输安全依赖 HTTPS。另外,部分老系统用 POST 做查询(参数放 Body),不符合 REST 语义,但技术上可行。
1.6 HTTP 状态码:场景举例
状态码是服务器告诉客户端「这次请求结果如何」的三位数字。先看第一位就能大致判断:
| 首位 | 类别 | 含义 | 谁的问题? |
|---|---|---|---|
| 1 | 信息性 | 收到,继续处理 | --- |
| 2 | 成功 | 请求成功 | ✅ 正常 |
| 3 | 重定向 | 需要去另一个地址 | 通常正常 |
| 4 | 客户端错误 | 请求有问题 | ❌ (前端/调用方) |
| 5 | 服务器错误 | 服务端出错了 | ❌ 后端/运维 |
遇频率说明(下文示例中标注):
| 标记 | 含义 |
|---|---|
| ⭐⭐⭐ | 日常开发必遇,几乎每个人都见过 |
| ⭐⭐ | 做 API/上传/权限时会遇到 |
| ⭐ | 特定场景才出现,知道即可 |
#mermaid-svg-sbAqgi8iAZ2oiV3n{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sbAqgi8iAZ2oiV3n .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sbAqgi8iAZ2oiV3n .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sbAqgi8iAZ2oiV3n .error-icon{fill:#552222;}#mermaid-svg-sbAqgi8iAZ2oiV3n .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sbAqgi8iAZ2oiV3n .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sbAqgi8iAZ2oiV3n .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sbAqgi8iAZ2oiV3n .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sbAqgi8iAZ2oiV3n .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sbAqgi8iAZ2oiV3n .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sbAqgi8iAZ2oiV3n .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sbAqgi8iAZ2oiV3n .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sbAqgi8iAZ2oiV3n .marker.cross{stroke:#333333;}#mermaid-svg-sbAqgi8iAZ2oiV3n svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sbAqgi8iAZ2oiV3n p{margin:0;}#mermaid-svg-sbAqgi8iAZ2oiV3n .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sbAqgi8iAZ2oiV3n .cluster-label text{fill:#333;}#mermaid-svg-sbAqgi8iAZ2oiV3n .cluster-label span{color:#333;}#mermaid-svg-sbAqgi8iAZ2oiV3n .cluster-label span p{background-color:transparent;}#mermaid-svg-sbAqgi8iAZ2oiV3n .label text,#mermaid-svg-sbAqgi8iAZ2oiV3n span{fill:#333;color:#333;}#mermaid-svg-sbAqgi8iAZ2oiV3n .node rect,#mermaid-svg-sbAqgi8iAZ2oiV3n .node circle,#mermaid-svg-sbAqgi8iAZ2oiV3n .node ellipse,#mermaid-svg-sbAqgi8iAZ2oiV3n .node polygon,#mermaid-svg-sbAqgi8iAZ2oiV3n .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sbAqgi8iAZ2oiV3n .rough-node .label text,#mermaid-svg-sbAqgi8iAZ2oiV3n .node .label text,#mermaid-svg-sbAqgi8iAZ2oiV3n .image-shape .label,#mermaid-svg-sbAqgi8iAZ2oiV3n .icon-shape .label{text-anchor:middle;}#mermaid-svg-sbAqgi8iAZ2oiV3n .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sbAqgi8iAZ2oiV3n .rough-node .label,#mermaid-svg-sbAqgi8iAZ2oiV3n .node .label,#mermaid-svg-sbAqgi8iAZ2oiV3n .image-shape .label,#mermaid-svg-sbAqgi8iAZ2oiV3n .icon-shape .label{text-align:center;}#mermaid-svg-sbAqgi8iAZ2oiV3n .node.clickable{cursor:pointer;}#mermaid-svg-sbAqgi8iAZ2oiV3n .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sbAqgi8iAZ2oiV3n .arrowheadPath{fill:#333333;}#mermaid-svg-sbAqgi8iAZ2oiV3n .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sbAqgi8iAZ2oiV3n .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sbAqgi8iAZ2oiV3n .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sbAqgi8iAZ2oiV3n .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sbAqgi8iAZ2oiV3n .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sbAqgi8iAZ2oiV3n .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sbAqgi8iAZ2oiV3n .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sbAqgi8iAZ2oiV3n .cluster text{fill:#333;}#mermaid-svg-sbAqgi8iAZ2oiV3n .cluster span{color:#333;}#mermaid-svg-sbAqgi8iAZ2oiV3n div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sbAqgi8iAZ2oiV3n .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sbAqgi8iAZ2oiV3n rect.text{fill:none;stroke-width:0;}#mermaid-svg-sbAqgi8iAZ2oiV3n .icon-shape,#mermaid-svg-sbAqgi8iAZ2oiV3n .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sbAqgi8iAZ2oiV3n .icon-shape p,#mermaid-svg-sbAqgi8iAZ2oiV3n .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sbAqgi8iAZ2oiV3n .icon-shape .label rect,#mermaid-svg-sbAqgi8iAZ2oiV3n .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sbAqgi8iAZ2oiV3n .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sbAqgi8iAZ2oiV3n .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sbAqgi8iAZ2oiV3n :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 5xx 服务器错误 ⭐⭐⭐
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
4xx 客户端错误 ⭐⭐⭐
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
3xx 重定向 ⭐⭐
301 Moved Permanently
302 Found
304 Not Modified
2xx 成功 ⭐⭐⭐
200 OK
201 Created
204 No Content
1xx 信息性 ⭐
100 Continue
一眼速查(带举例)
| 状态码 | 含义 | 举例(你什么时候会看到) | 频率 |
|---|---|---|---|
| 200 | 成功 | 登录成功返回用户信息;查询订单列表 | ⭐⭐⭐ |
| 201 | 已创建 | POST 新建客户/订单,资源创建成功 | ⭐⭐⭐ |
| 204 | 成功,无内容 | DELETE 删除一条记录,不需要返回 body | ⭐⭐ |
| 301 | 永久搬家 | 旧域名 http://old.com 永久跳到 https://new.com |
⭐⭐ |
| 304 | 用缓存 | 刷新页面时,CSS 没改过,浏览器直接用本地缓存 | ⭐⭐ |
| 400 | 参数错了 | 手机号少一位、JSON 少逗号、必填字段没传 | ⭐⭐⭐ |
| 401 | 没登录/Token 失效 | Token 过期调接口;没带 Authorization 头 | ⭐⭐⭐ |
| 403 | 没权限 | 普通员工访问「仅管理员」接口 | ⭐⭐⭐ |
| 404 | 找不到 | URL 写错 /api/usres;订单 ID 不存在 |
⭐⭐ |
| 500 | 服务器崩了 | 后端空指针、SQL 报错、未捕获异常 | ⭐⭐⭐ |
| 502 | 网关连不上后端 | Nginx 后面的 Java 服务挂了 | ⭐⭐ |
| 503 | 服务暂不可用 | 系统维护中;流量太大被限流熔断 | ⭐⭐ |
| 504 | 请求超时 | 504 Gateway Timeout ,是后端服务(Tomcat/Node)处理请求太慢了,超过了中间层(如 Nginx)设置的等待时间 | ⭐⭐⭐ |
下面按类别展开,每个状态码用真实场景 + 请求/响应示例说明,帮助理解「什么情况下会遇到」。
1xx 信息性响应(⭐ 很少直接感知)
这类状态码你在浏览器里几乎看不到,大文件上传、WebSocket 升级时才会涉及。
100 Continue ⭐
场景: 上传 500MB 文件前,客户端先问服务器「我准备发大 body 了,你要吗?」,服务器说「继续发」。
http
# 客户端先发请求头
PUT /api/files/big.zip HTTP/1.1
Expect: 100-continue
# 服务器同意
HTTP/1.1 100 Continue
# 客户端再发送文件内容...
你什么时候遇到: 自己写大文件上传客户端,或用 curl -L 上传超大文件时。日常调 JSON 接口基本不会见到。
101 Switching Protocols ⭐
场景: 网页从 HTTP 升级到 WebSocket 长连接(在线聊天、实时通知)。
http
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
你什么时候遇到: 做 WebSocket 开发时;普通 REST API 不会遇到。
2xx 成功(⭐⭐⭐ 最常见)
200 OK ⭐⭐⭐
场景 1: 查询用户列表成功
http
GET /api/users?page=1 HTTP/1.1
Authorization: Bearer eyJhbGci...
HTTP/1.1 200 OK
Content-Type: application/json
{"data": [{"id": 1, "name": "张三"}], "total": 100}
场景 2: 登录成功(也有接口用 200 而非 201)
http
POST /api/login HTTP/1.1
Content-Type: application/json
{"username": "admin", "password": "123456"}
HTTP/1.1 200 OK
{"token": "eyJhbG...", "userId": 1}
⚠️ 注意陷阱: 有些接口 HTTP 返回 200,但 body 里写 "code": 500, "message": "余额不足"。所以要同时看状态码和 body。
json
// HTTP 200,但业务失败 ------ 很多国内接口这样设计
{"code": 40001, "message": "订单不存在", "success": false}
201 Created ⭐⭐⭐
场景: 新建一条销售订单、创建一个客户
http
POST /api/sales-orders HTTP/1.1
Content-Type: application/json
{"customerId": 100, "items": [{"sku": "A001", "qty": 2}]}
HTTP/1.1 201 Created
Location: /api/sales-orders/9527
Content-Type: application/json
{"id": 9527, "status": "pending", "createdAt": "2026-07-06T10:00:00Z"}
和 200 的区别: 201 专用于「新资源被创建 」,响应头里常有 Location 指向新资源地址。
204 No Content ⭐⭐
场景: 删除一条记录、更新设置(不需要返回任何数据)
http
DELETE /api/users/42 HTTP/1.1
Authorization: Bearer eyJhbG...
HTTP/1.1 204 No Content
(响应体为空)
前端常见坑:
javascript
// ❌ 204 没有 body,这样写会报错
const data = await response.json(); // Unexpected end of JSON input
// ✅ 正确写法
if (response.status === 204) {
console.log('删除成功');
} else {
const data = await response.json();
}
206 Partial Content ⭐
场景: 视频播放拖动进度条、断点续传下载
http
GET /videos/demo.mp4 HTTP/1.1
Range: bytes=0-1023
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/1048576
Content-Length: 1024
(只返回文件的第 0~1023 字节)
你什么时候遇到: 看视频拖进度条、下载工具「续传」时。调普通 JSON 接口不会遇到。
3xx 重定向(⭐⭐ 浏览器常见,API 调用需注意)
301 Moved Permanently ⭐⭐
场景: 公司换了域名,旧地址永久跳转到新地址
http
GET http://old-company.com/api/users HTTP/1.1
HTTP/1.1 301 Moved Permanently
Location: https://new-company.com/api/users
举例: 访问 http://example.com 被永久跳到 https://example.com(HTTP → HTTPS)。
你该怎么做: 更新代码里的 baseURL,不要长期依赖 301 跳转。
302 Found ⭐⭐
场景: 未登录访问后台,被临时跳到登录页
http
GET /admin/dashboard HTTP/1.1
Cookie: (无 session)
HTTP/1.1 302 Found
Location: /login?redirect=/admin/dashboard
和 301 的区别: 302 是临时跳转,以后还可能变回原地址。
⚠️ 经典坑: 旧浏览器遇到 302 时,可能把 POST 变成 GET ,导致提交的数据丢失。现代 API 更推荐用 307/308。
304 Not Modified ⭐⭐
场景: 浏览器第二次加载网页,问服务器「我本地的 app.js 改过吗?」,服务器说「没改,用你缓存的就行」
http
GET /static/app.js HTTP/1.1
If-None-Match: "abc123"
If-Modified-Since: Wed, 01 Jul 2026 10:00:00 GMT
HTTP/1.1 304 Not Modified
(无 body,浏览器直接用本地缓存)
你什么时候遇到: 打开 DevTools → Network,看到某些静态资源 Status 是 304。说明缓存生效,不是错误。
开发排查: 「我明明改了代码,页面怎么还是旧的?」→ 强刷 Ctrl+F5 或禁用缓存。
4xx 客户端错误(⭐⭐⭐ 开发中最常排查)
4xx = 你的请求有问题,优先检查:URL、参数、Header、Token、权限。
400 Bad Request ⭐⭐⭐
场景 1: JSON 格式错误
http
POST /api/users HTTP/1.1
Content-Type: application/json
{"name": "张三", "age": } ← 少了值,JSON 非法
HTTP/1.1 400 Bad Request
{"error": "Invalid JSON at line 1 column 25"}
场景 2: 必填参数缺失
http
POST /api/users HTTP/1.1
{"name": "张三"} ← 缺少必填字段 email
HTTP/1.1 400 Bad Request
{"error": "Validation failed", "fields": {"email": "不能为空"}}
场景 3: 类型不对
http
POST /api/users HTTP/1.1
{"name": "张三", "age": "二十五"} ← age 应该是数字
HTTP/1.1 400 Bad Request
{"error": "age must be a number"}
怎么排查: 对照 API 文档;用 Postman 对比正确请求;看响应 body 里的字段级错误。
401 Unauthorized ⭐⭐⭐
场景 1: 没带 Token
http
GET /api/orders HTTP/1.1
(没有 Authorization 头)
HTTP/1.1 401 Unauthorized
{"error": "Missing authentication token"}
场景 2: Token 过期
http
GET /api/orders HTTP/1.1
Authorization: Bearer eyJhbGci...(已过期)
HTTP/1.1 401 Unauthorized
{"error": "Token expired", "code": "TOKEN_EXPIRED"}
场景 3: 登录账号密码错误(有些接口也返回 401)
http
POST /api/login HTTP/1.1
{"username": "admin", "password": "wrong"}
HTTP/1.1 401 Unauthorized
{"error": "Invalid username or password"}
怎么排查: 检查 Authorization 头格式是否为 Bearer <token>;Token 是否过期;是否需要先调登录接口。
403 Forbidden ⭐⭐⭐
场景: 已登录,但没有权限操作
http
DELETE /api/admin/users/1 HTTP/1.1
Authorization: Bearer eyJhbGci...(普通员工 Token)
HTTP/1.1 403 Forbidden
{"error": "Permission denied", "required": "admin"}
和 401 的区别(必记):
| 状态码 | 含义 | 生活比喻 |
|---|---|---|
| 401 | 不知道你是谁 | 没刷工牌就进公司 |
| 403 | 知道你是谁,但不让你做 | 刷了工牌,但你是访客不能进机房 |
404 Not Found ⭐⭐⭐
场景 1: URL 路径写错
http
GET /api/usres/1 HTTP/1.1 ← users 拼成了 usres
HTTP/1.1 404 Not Found
{"error": "Route not found"}
场景 2: 资源已被删除或 ID 不存在
http
GET /api/orders/999999 HTTP/1.1
HTTP/1.1 404 Not Found
{"error": "Order 999999 not found"}
怎么排查: 核对 URL 拼写、HTTP 方法、资源 ID 是否存在。
405 Method Not Allowed ⭐⭐
场景: 对接口用了错误的 HTTP 方法
http
GET /api/users HTTP/1.1 ← 创建用户应该用 POST
HTTP/1.1 405 Method Not Allowed
Allow: POST
{"error": "Method GET not allowed, use POST"}
怎么排查: 看响应头 Allow 字段,确认支持的 Method。
408 Request Timeout ⭐
场景: 客户端发送请求体太慢,服务器等不及了
你什么时候遇到: 网络极差时上传大文件;物联网弱网环境。一般后端框架较少主动返回 408。
409 Conflict ⭐⭐
场景 1: 重复创建(邮箱/用户名唯一)
http
POST /api/users HTTP/1.1
{"email": "zhangsan@example.com"} ← 该邮箱已存在
HTTP/1.1 409 Conflict
{"error": "Email already exists"}
场景 2: 乐观锁版本冲突(两人同时编辑)
http
PUT /api/orders/9527 HTTP/1.1
{"status": "shipped", "version": 3} ← 服务器已是 version 4
HTTP/1.1 409 Conflict
{"error": "Resource was modified by another user", "currentVersion": 4}
场景 3: 重复提交订单(没做幂等)
http
POST /api/orders HTTP/1.1
Idempotency-Key: order-20260706-001 ← 同一 Key 提交了两次
HTTP/1.1 409 Conflict
{"error": "Duplicate request"}
413 Payload Too Large ⭐⭐
场景: 上传的文件超过服务器限制
http
POST /api/upload HTTP/1.1
Content-Type: multipart/form-data
(上传 200MB 文件,服务器限制 50MB)
HTTP/1.1 413 Payload Too Large
{"error": "File size exceeds 50MB limit"}
怎么解决: 压缩文件;分片上传;让运维调大 Nginx client_max_body_size。
415 Unsupported Media Type ⭐⭐
场景: Content-Type 和服务端期望的不一致
http
POST /api/users HTTP/1.1
Content-Type: text/plain ← 服务端只接受 JSON
{"name": "张三"}
HTTP/1.1 415 Unsupported Media Type
{"error": "Content-Type must be application/json"}
常见原因: 发了 JSON 但 Header 写成了 application/x-www-form-urlencoded。
422 Unprocessable Entity ⭐⭐
场景: JSON 格式正确,但业务规则不满足
http
POST /api/orders HTTP/1.1
{"productId": 1, "quantity": -5} ← 数量不能为负数
HTTP/1.1 422 Unprocessable Entity
{"error": "Validation failed", "fields": {"quantity": "must be greater than 0"}}
和 400 的区别: 400 偏「格式/语法错误」;422 偏「格式对,但语义/业务规则不通过」。很多框架两者混用,知道即可。
429 Too Many Requests ⭐⭐
场景: 接口被限流(防刷、API 配额)
http
GET /api/data HTTP/1.1
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{"error": "Rate limit exceeded, max 100 requests per minute"}
举例: 1 分钟内调了 200 次接口;短信验证码连点 5 次;爬虫被抓。
怎么解决: 等 Retry-After 秒数后重试;加缓存;降低调用频率。
5xx 服务器错误(⭐⭐⭐ 需要后端/运维排查)
5xx = 服务端的问题,前端通常无法自行修复,应记录请求信息并联系后端。
500 Internal Server Error ⭐⭐⭐
场景 1: 后端代码 bug(空指针)
http
GET /api/orders/9527 HTTP/1.1
HTTP/1.1 500 Internal Server Error
{"error": "Internal server error"} ← 生产环境通常不暴露细节
场景 2: 数据库连接失败
后端日志:Connection refused: mysql://localhost:3306
前端看到:500 Internal Server Error
场景 3: 未捕获的异常
python
# 后端代码
name = order.customer.name # customer 是 null → 500
502 Bad Gateway ⭐⭐
场景: Nginx/网关前面的服务正常,但后面的应用挂了
用户 → Nginx(正常)→ Java 应用(进程挂了)→ 502
http
GET /api/users HTTP/1.1
HTTP/1.1 502 Bad Gateway
<html><body>502 Bad Gateway</body></html>
常见原因: 后端进程崩溃;Docker 容器重启中;upstream 地址配错;端口不通。
和 500 的区别: 500 是应用自己 报错;502 是网关连不上应用。
503 Service Unavailable ⭐⭐
场景 1: 系统维护
http
GET /api/users HTTP/1.1
HTTP/1.1 503 Service Unavailable
Retry-After: 3600
{"error": "System maintenance, back at 18:00"}
场景 2: 过载/熔断
流量突增 → 服务器扛不住 → 主动返回 503 保护系统
你怎么应对: 看 Retry-After 头;稍后重试;联系运维确认是否在维护。
504 Gateway Timeout ⭐⭐
场景: 后端处理太慢,网关等超时了
http
POST /api/reports/generate HTTP/1.1 ← 生成报表要 5 分钟
HTTP/1.1 504 Gateway Timeout
{"error": "Gateway timeout after 60s"}
常见原因: 慢 SQL;导出大数据;第三方 API 响应慢;Nginx proxy_read_timeout 太短。
怎么解决: 优化后端性能;改为异步任务(先返回 202,完成后通知);调大网关超时。
507 Insufficient Storage ⭐
场景: 服务器磁盘满了,无法写入
你什么时候遇到: 自建服务器、网盘类应用。云 SaaS 较少直接暴露 507。
在 Chrome DevTools 里看状态码
-
按
F12打开开发者工具 -
切到 Network(网络) 标签
-
刷新页面或触发请求
-
看 Status 列:
200绿色、3xx/4xx/5xx通常橙色或红色Name Status Type
users 200 xhr ← 成功
login 401 xhr ← Token 失效,要重新登录
orders/999 404 xhr ← 订单不存在
upload 413 xhr ← 文件太大
api/data 502 xhr ← 后端挂了,找运维
1.7 HTTP vs HTTPS
| 特性 | HTTP | HTTPS |
|---|---|---|
| 端口 | 80 | 443 |
| 加密 | 明文传输 | TLS/SSL 加密 |
| 证书 | 不需要 | 需要 CA 证书 |
| 安全性 | 低(可被窃听/篡改) | 高 |
| 性能 | 无 TLS 握手开销 | 首次连接需 TLS 握手(约 1~2 RTT) |
HTTPS(超文本传输安全协议) 就是 HTTP 的安全加强版 。简单说,它等于 HTTP + SSL/TLS 加密层。
它的核心价值在于解决了 HTTP 最致命的缺陷------明文传输。在 HTTP 下,你的账号密码、聊天记录在网络中像"裸奔"一样能被任何人窃取或篡改,而 HTTPS 通过加密手段,将这些信息装进了一个只有收发双方才能打开的"保险箱"里。
具体来说,HTTPS 做了三件关键的事:
-
数据加密(防窃听):将传输内容打乱成乱码,即使黑客截获了数据包,看到的也是一堆无意义的密文。
-
数据完整性(防篡改):确保数据在传输过程中没有被中间人偷偷修改或调包。
-
身份验证(防冒充) :通过数字证书验证服务器确实是它声称的那个网站(比如你访问银行网站,证书能证明它真的是银行,而不是钓鱼网站)。
为了实现这些,HTTPS 采用了混合加密 策略:握手阶段 使用非对称加密 (公钥/私钥)安全地交换密钥;数据传输阶段 则改用对称加密,这种算法计算量小、速度快,保证了用户体验。
#mermaid-svg-6BDX3ypdPyLBWxb0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6BDX3ypdPyLBWxb0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6BDX3ypdPyLBWxb0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6BDX3ypdPyLBWxb0 .error-icon{fill:#552222;}#mermaid-svg-6BDX3ypdPyLBWxb0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6BDX3ypdPyLBWxb0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6BDX3ypdPyLBWxb0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6BDX3ypdPyLBWxb0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6BDX3ypdPyLBWxb0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6BDX3ypdPyLBWxb0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6BDX3ypdPyLBWxb0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6BDX3ypdPyLBWxb0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6BDX3ypdPyLBWxb0 .marker.cross{stroke:#333333;}#mermaid-svg-6BDX3ypdPyLBWxb0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6BDX3ypdPyLBWxb0 p{margin:0;}#mermaid-svg-6BDX3ypdPyLBWxb0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6BDX3ypdPyLBWxb0 .cluster-label text{fill:#333;}#mermaid-svg-6BDX3ypdPyLBWxb0 .cluster-label span{color:#333;}#mermaid-svg-6BDX3ypdPyLBWxb0 .cluster-label span p{background-color:transparent;}#mermaid-svg-6BDX3ypdPyLBWxb0 .label text,#mermaid-svg-6BDX3ypdPyLBWxb0 span{fill:#333;color:#333;}#mermaid-svg-6BDX3ypdPyLBWxb0 .node rect,#mermaid-svg-6BDX3ypdPyLBWxb0 .node circle,#mermaid-svg-6BDX3ypdPyLBWxb0 .node ellipse,#mermaid-svg-6BDX3ypdPyLBWxb0 .node polygon,#mermaid-svg-6BDX3ypdPyLBWxb0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6BDX3ypdPyLBWxb0 .rough-node .label text,#mermaid-svg-6BDX3ypdPyLBWxb0 .node .label text,#mermaid-svg-6BDX3ypdPyLBWxb0 .image-shape .label,#mermaid-svg-6BDX3ypdPyLBWxb0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-6BDX3ypdPyLBWxb0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6BDX3ypdPyLBWxb0 .rough-node .label,#mermaid-svg-6BDX3ypdPyLBWxb0 .node .label,#mermaid-svg-6BDX3ypdPyLBWxb0 .image-shape .label,#mermaid-svg-6BDX3ypdPyLBWxb0 .icon-shape .label{text-align:center;}#mermaid-svg-6BDX3ypdPyLBWxb0 .node.clickable{cursor:pointer;}#mermaid-svg-6BDX3ypdPyLBWxb0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6BDX3ypdPyLBWxb0 .arrowheadPath{fill:#333333;}#mermaid-svg-6BDX3ypdPyLBWxb0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6BDX3ypdPyLBWxb0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6BDX3ypdPyLBWxb0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6BDX3ypdPyLBWxb0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6BDX3ypdPyLBWxb0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6BDX3ypdPyLBWxb0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6BDX3ypdPyLBWxb0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6BDX3ypdPyLBWxb0 .cluster text{fill:#333;}#mermaid-svg-6BDX3ypdPyLBWxb0 .cluster span{color:#333;}#mermaid-svg-6BDX3ypdPyLBWxb0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6BDX3ypdPyLBWxb0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6BDX3ypdPyLBWxb0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-6BDX3ypdPyLBWxb0 .icon-shape,#mermaid-svg-6BDX3ypdPyLBWxb0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6BDX3ypdPyLBWxb0 .icon-shape p,#mermaid-svg-6BDX3ypdPyLBWxb0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6BDX3ypdPyLBWxb0 .icon-shape .label rect,#mermaid-svg-6BDX3ypdPyLBWxb0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6BDX3ypdPyLBWxb0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6BDX3ypdPyLBWxb0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6BDX3ypdPyLBWxb0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTTPS(加密)
HTTP
TLS 加密层
TCP
HTTP(明文)
HTTP
TCP
日期:2026-07-10