常见 HTTP 请求头:从“为什么接口返回乱码”说起

在调试一个跨域上传接口时,后端同事突然找你:"你传的文件名是乱码啊!"

你一脸懵:"我传的是 简历-张三.pdf,怎么就乱码了?"

查了半天,发现是漏了一个关键请求头:

js 复制代码
// ❌ 错误写法
fetch('/upload', {
  method: 'POST',
  body: formData
})

// ✅ 正确写法
fetch('/upload', {
  method: 'POST',
  headers: {
    'Content-Type': 'multipart/form-data; charset=utf-8' // 🔍 指定字符集
  },
  body: formData
})

一、问题场景:文件上传中的中文乱码

有个简历上传功能,用户上传 简历-张三.pdf,但后端收到的是 简历-å¼ ä¸‰.pdf

问题出在哪?------ 字符编码未声明

HTTP 协议本身是 ASCII 的,但现代 Web 处理的是 UTF-8 中文。如果没有明确告诉服务器"这是 UTF-8",它可能按 ISO-8859-1 解码,结果就是乱码。


二、解决方案:用正确的请求头声明编码

js 复制代码
// 方案1:在 Content-Type 中声明
const formData = new FormData()
formData.append('file', fileInput.files[0])
formData.append('filename', '简历-张三.pdf')

fetch('/api/upload', {
  method: 'POST',
  headers: {
    // 🔍 明确指定字符集
    'Content-Type': 'multipart/form-data; charset=utf-8'
  },
  body: formData
})

Content-Typecharsetmultipart/form-data 实际上不起作用!真正的解决方案是:

js 复制代码
// 方案2:在字段名中使用 RFC 5987 编码
formData.append('filename*', 'UTF-8\'\'%E7%AE%80%E5%8E%86-%E5%BC%A0%E4%B8%89.pdf')

或者让后端支持 UTF-8 解码 multipart 表单。


三、核心请求头详解:从表面到协议层的三层机制

1. 表面用法:最常用的 10 个请求头

请求头 作用 示例
Host 指定服务器域名和端口 Host: api.example.com:8080
User-Agent 客户端标识 User-Agent: Chrome/128.0
Accept 客户端能接收的响应类型 Accept: application/json
Accept-Encoding 支持的压缩格式 Accept-Encoding: gzip, deflate
Accept-Language 期望的语言 Accept-Language: zh-CN,zh;q=0.9
Content-Type 请求体的 MIME 类型 Content-Type: application/json; charset=utf-8
Authorization 身份认证凭证 Authorization: Bearer <token>
Referer 来源页面 Referer: https://example.com/search
Origin 跨域请求的源 Origin: https://admin.example.com
Cookie 发送 Cookie Cookie: session=abc123

2. 底层机制:浏览器如何使用这些请求头

我们来画一张 浏览器发送请求的流程图

flowchart TD A([1. 用户输入 URL 或发起 fetch]) --> B[2. 浏览器自动添加基础头] B --> C[3. 开发者手动添加/覆盖 headers] C --> D[4. 发送请求] D --> E[5. 服务器根据请求头决定] B --> B1["Host(从 URL 解析)"]:::auto B --> B2["User-Agent(内置)"]:::auto B --> B3["Accept(默认 */*)"]:::auto B --> B4["Accept-Encoding(gzip 等)"]:::auto B --> B5["Accept-Language(系统语言)"]:::auto B --> B6["Cookie(匹配域名的)"]:::auto E --> E1["返回格式(JSON/HTML)"]:::server E --> E2["是否压缩响应"]:::server E --> E3["使用什么语言"]:::server E --> E4["是否允许跨域"]:::server E --> E5["是否认证通过"]:::server classDef auto fill:#d4edda,stroke:#333 classDef server fill:#ffd699,stroke:#333 classDef default fill:#f8f9fa,stroke:#333

关键点:

  • Host 是 HTTP/1.1 强制要求的头,用于虚拟主机
  • User-Agent 被用于设备识别、兼容性处理
  • Accept-Encoding 触发服务器压缩响应,节省带宽

3. 设计哲学:为什么需要这么多请求头?

HTTP 是"无状态、可扩展 "的协议,请求头就是它的扩展机制

类比:

HTTP 请求就像一封挂号信,请求头是信封上的标签:

  • Host → 收件人地址
  • User-Agent → 寄件人身份
  • Accept → "请用中文回复"
  • Authorization → "我是 VIP,优先处理"
  • Content-Type → "信纸是 PDF 格式"

没有这些标签,邮局(服务器)就不知道怎么处理这封信。


四、实战避坑指南

❌ 错误1:忘记设置 Content-Type

js 复制代码
// ❌ 后端可能无法解析
fetch('/api/user', {
  method: 'POST',
  body: JSON.stringify({ name: 'Alice' })
})

// ✅ 必须声明
fetch('/api/user', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json' // 🔍 告诉后端这是 JSON
  },
  body: JSON.stringify({ name: 'Alice' })
})

❌ 错误2:跨域请求缺少 Origin

js 复制代码
// ❌ 手动设置 Origin 可能被阻止
fetch('https://api.other.com/data', {
  headers: {
    'Origin': 'https://myapp.com' // 浏览器会忽略!
  }
})

// ✅ 浏览器自动添加 Origin(在跨域请求时)
fetch('https://api.other.com/data') // 自动带 Origin

📌 注意:Origin 由浏览器在跨域请求时自动添加,不能手动设置。

❌ 错误3:压缩未启用

js 复制代码
// ❌ 默认可能不压缩
fetch('/api/large-data')

// ✅ 显式声明支持压缩(现代浏览器默认已加)
// 浏览器自动加: Accept-Encoding: gzip, deflate, br

五、高级用法:条件请求与缓存

1. If-None-Match / ETag:高效缓存

js 复制代码
// 第一次请求
// ← 响应头: ETag: "abc123"

// 第二次请求带上 ETag
fetch('/api/config', {
  headers: {
    'If-None-Match': '"abc123"'
  }
})
// ← 如果未修改,返回 304 Not Modified(无响应体)

2. If-Modified-Since:基于时间的缓存

js 复制代码
fetch('/api/report', {
  headers: {
    'If-Modified-Since': 'Wed, 28 Jul 2025 08:00:00 GMT'
  }
})

六、对比主流场景下的请求头组合

场景 关键请求头 说明
JSON API 请求 Content-Type: application/json Authorization: Bearer ... 标准 REST API
表单提交 Content-Type: application/x-www-form-urlencoded 传统 form 提交
文件上传 Content-Type: multipart/form-data 支持文件和字段混合
跨域请求 Origin Authorization(触发预检) 触发 CORS 预检
服务端请求 User-Agent: MyApp/1.0 X-API-Key: ... 区分客户端,用 API Key 认证

七、举一反三:三个变体场景实现思路

  1. 需要实现"断点续传"

    使用 Range: bytes=0-1023 请求头,服务器返回 206 Partial Content

  2. 防止 CSRF 攻击

    前端在请求头中添加 X-Requested-With: XMLHttpRequest 或自定义头,后端验证。

  3. A/B 测试环境切换

    通过 X-Environment: beta 请求头,让网关路由到测试环境。

js 复制代码
fetch('/api/data', {
  headers: {
    'X-Environment': 'beta',
    'X-Request-ID': generateId() // 🔍 用于链路追踪
  }
})

小结

HTTP 请求头不是"可有可无的配置",而是客户端与服务器沟通的"业务语言"

*传数据,定类型(Content-Type);
要认证,加 Authorization;
跨域时,看 Origin;
想缓存,用 ETag;
中文名,编码传(filename
)。**

相关推荐
求知若渴,虚心若愚。3 小时前
Error reading config file (/home/ansible.cfg): ‘ACTION_WARNINGS(default) = True
linux·前端·ansible
LinDaiuuj4 小时前
最新的前端技术和趋势(2025)
前端
一只小风华~4 小时前
JavaScript 函数
开发语言·前端·javascript·ecmascript·web
程序猿阿伟5 小时前
《不只是接口:GraphQL与RESTful的本质差异》
前端·restful·graphql
若梦plus7 小时前
Nuxt.js基础与进阶
前端·vue.js
樱花开了几轉7 小时前
React中为甚么强调props的不可变性
前端·javascript·react.js
风清云淡_A7 小时前
【REACT18.x】CRA+TS+ANTD5.X实现useImperativeHandle让父组件修改子组件的数据
前端·react.js
小飞大王6667 小时前
React与Rudex的合奏
前端·react.js·前端框架
若梦plus7 小时前
React之react-dom中的dom-server与dom-client
前端·react.js
若梦plus7 小时前
react-router-dom中的几种路由详解
前端·react.js