前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库fetch POST FormData JSON Content-Type boundary
浏览浏览器请求与通信 API

fetch 发送 POST 请求时,FormData 与 JSON body 在 Content-Type 和处理上有什么区别?

发送 POST 时,FormData 自动获得含 boundary 的 multipart/form-data Content-Type,而 JSON body 需要手动声明 application/json 并序列化为字符串。

前端进阶之旅 · 一题精讲更新于 2026.09.05
浏览器#请求与通信 API
先看核心答案
理解线索

request body 编码机制

  1. FormData 编码浏览器原生编码为 multipart/form-data
  2. JSON 字符串需 JSON.stringify 并声明媒体类型
  3. 边界随机串boundary 由浏览器生成,头与体一一对应

服务端依据 Content-Type 决定如何解析 body,两者格式完全不同。

核心回答

先记住这个答案

fetch 的 body 传 FormData 实例时,浏览器自动设置 Content-Type 为 multipart/form-data,并附带随机 boundary 分隔各字段;手动设置会丢失 boundary,导致服务端解析失败。若传 JSON 字符串,需显式设置 headers 中的 Content-Type 为 application/json,否则服务端可能按纯文本处理。处理上,服务端需按对应 MIME 类型解析,前端还需注意序列化与 CORS 预检。

  • FormData 自动生成 multipart boundary
  • JSON body 必须手动声明为 application/json
  • 手动给 FormData 设 Content-Type 会致解析失败

编码与头部差异的机制

当 fetch 的 body 选项传入 FormData 实例时,浏览器会将整个请求体重写为 multipart/form-data 编码,并在请求头自动附加 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXX。boundary 是随机生成的字符串,用于分隔每个表单字段的边界,服务端依赖它切分二进制与文本。因为头部和体强关联,任何手动设置的 Content-Type 都可能与真实 body 不匹配,所以标准做法是不写该头。

JSON 则不同,JSON.stringify 得到的字符串没有固有媒体类型,因此必须手动在 headers 中设置 Content-Type: application/json。若省略,许多服务端默认按 text/plain 或 application/octet-stream 处理,导致 req.body 为空或解析异常。同样,还需关注 charset,如 application/json; charset=utf-8,但通常只写主类型即可。

表单提交含文件与普通文本的工程场景

某后台管理页面需要一次性提交一个标题(字符串)、一张用户头像(Blob)和一组自定义元数据对象。若使用 JSON,必须先把二进制转为 base64 或 dataURL,会让体积增加约 33% 且后端解码复杂。改用 FormData:append('title', 'CEO'), append('avatar', fileInput.files[0]), append('meta', JSON.stringify(meta)),此时无需任何 Content-Type 设置,fetch 自动生成 multipart 头。

处理结果:请求头含 boundary,node.js 的 multer 或 busboy 能正确解析文件字段和文本字段;meta 需后端自行 JSON.parse。若误以为需要手动写 Content-Type: multipart/form-data,会发现丢失 boundary 的 400 错误。该场景的约束是文件与普通字段混发,选 FormData 能减少序列化逻辑,但代价是额外 multipart 开销。

手动设置失效的条件与替代

FormData 的手动 Content-Type 问题只在浏览器原生隔离下明显:你无法从外部拿到确切 boundary,因此设置一个不匹配的静态头必然破坏数据包。有些测试库模拟 fetch 时允许覆盖,但真实浏览器允许设置却不会自动补充缺失的 boundary,导致头与体不匹配。若需要自定义头,必须完全自己构造 multipart 格式,或改用 Blob 的 type 属性,但仍可能不一致。

JSON 的边界在于服务端对缺失 Content-Type 的容忍度不同:某些框架(如 Express 的 express.json)要求严格匹配,否则可能返回 415 或无法解析。处理做法是在服务端同时支持两种解析,或在客户端统一约定;若无法控制服务端,必须显式设置 application/json。同时跨域时,application/json 会触发 CORS 预检请求,需要服务端允许该头,而 FormData 的简单请求不需预检(若 method 为 POST 且无自定义头)。

回答前,多想一步

容易答错的地方

手动给 FormData 设置 Content-Type 头
错误认为 multipart/form-data 必须像 JSON 一样显式设置。实际上浏览器生成完整头部含 boundary,手动设置会丢失边界串,导致服务端无法分隔字段,请求体解析失败。正确做法是不动该头,交给 fetch 自动填充。
JSON body 忘记设置 Content-Type
很多初学者只把 JSON.stringify 结果传入 body,却不加任何 headers。服务端无法识别为 JSON,req.body 可能为空或变成字符串。需要显式 headers: {'Content-Type': 'application/json'},且字符串必须是合法 JSON,否则后端 JSON.parse 抛异常。
试着用自己的话回答

面试官还会怎么问?

如何确认浏览器自动生成的 Content-Type 实际值?

在浏览器 DevTools 的 Network 面板查看请求头,能看到完整值如 multipart/form-data; boundary=----WebKitFormBoundaryABC。也可在 fetch 前用 Headers 对象截获,但最终 boundary 由浏览器在发送时自动生成,无法提前读取。

服务端如何区分收到的是 JSON 还是 FormData?

按 Content-Type 判断:application/json 走 JSON 解析,multipart/form-data 用表单解析库。若无头,多数框架按普通文本处理;若两种头都有,需优先级约定,推荐强制服务端只认一种。

FormData 也可以发送纯 JSON 字段吗?为什么还要用 JSON body?

可以,把 JSON 序列化后放进字段,但服务端需额外 parse,通信体积更大,且不方便直接表达嵌套结构。JSON body 结构直接,适合 API 交换,而 FormData 更利于文件上传和传统表单提交。

从一道题,走向一组知识

把知识连起来

请求与通信 API

fetch 默认跨域请求为什么不带 Cookie,credentials 选项各取值有什么区别?

同属「请求与通信 API」专题,接着看 fetch credentials cookie same-origin include 在具体场景中的处理方式。

请求与通信 API

为什么 fetch 的 Response body 只能读取一次,需要读两次时怎么办?

同属「请求与通信 API」专题,接着看 Response bodyUsed 只能读一次 clone 在具体场景中的处理方式。

参考资料

  • Using the Fetch API

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 编码与头部差异的机制
  3. 表单提交含文件与普通文本的工程场景
  4. 手动设置失效的条件与替代
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑