JS向服务器传数据,核心不是把“类型”两个字发给服务器,而是先把数据序列化成服务器能解析的格式,再用Content-Type声明格式;对象和数组走JSON.stringify,文件走FormData,二进制走ArrayBuffer或Blob,简单键值对走URLSearchParams。
服务器眼里的“数据类型”,其实由Content-Type决定
你可以把Content-Type理解成快递面单,面单写错,包裹里的东西再对,对方也拆不明白,前端传参也一样:body里放什么格式,请求头就要声明什么格式。
四种常见Content-Type对照
| 数据形态 | 常用Content-Type | JS构造方式 | 典型场景 |
|---|---|---|---|
| 对象、数组、嵌套结构 | application/json |
JSON.stringify(obj) |
接口提交表单数据、配置项 |
| 文件、图片、混合字段 | multipart/form-data |
new FormData() |
头像上传、附件上传 |
| 简单键值对 | application/x-www-form-urlencoded |
new URLSearchParams() |
传统表单、登录参数 |
| 纯二进制 | application/octet-stream或具体MIME |
ArrayBuffer、Blob |
文件切片、音视频流 |
据MDN Web Docs的公开说明,fetch的body支持Blob、ArrayBuffer、FormData、URLSearchParams等类型,也就是说,JS侧能传的“数据类型”不少,关键是选对序列化方式。
为什么不能直接传对象
很多人第一次写fetch会这样干:
fetch('/api/save', {
method: 'POST',
body: { name: '张三' }
})
结果服务端收到的是[object Object],原因是body只认字符串、Blob、FormData这类内容,普通对象会被隐式转成字符串,正确做法是先JSON.stringify,再配application/json,这个问题在百度搜索里常被描述为“js传对象到后端变成object Object怎么解决”,本质就是漏了序列化。
js向服务器传数据用JSON还是FormData?看场景选型
这个疑问在项目里很常见,答案不复杂:看数据里有没有文件,看服务端怎么解析。
传对象、数组、嵌套结构:JSON是默认答案
操作步骤可以固定成四步:
- 把要传的数据组织成普通对象或数组。
- 用
JSON.stringify(data)转成字符串。 - 请求头设置
'Content-Type': 'application/json'。 - 把字符串放进
body。
const data = { name: '张三', tags: ['a', 'b'] };
fetch('/api/user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
服务端如果是Express,需要挂express.json()中间件;如果是其他框架,也要有对应的JSON解析配置,前端传了JSON,服务端没开解析,接口就会拿到空body,这类问题在联调中很常见。
传文件、图片、混合字段:FormData更省心
FormData的好处是能同时塞文件和普通字段,浏览器会自动生成带boundary的Content-Type,操作路径:
const fd = new FormData();fd.append('file', fileInput.files[0]);fd.append('userId', '123');fetch('/api/upload', { method: 'POST', body: fd });
这里有个高频坑:不要手动设置Content-Type: multipart/form-data,业内专家指出,手动设置会丢掉浏览器自动生成的boundary,服务端解析就会失败,用axios时也一样,传FormData让浏览器自己处理请求头。
传简单键值对:URLSearchParams别忽略
登录、筛选、分页这类简单参数,用URLSearchParams更轻:
const params = new URLSearchParams();
params.append('page', '1');
params.append('size', '20');
fetch('/api/list?' + params.toString());
POST表单场景也可以把它放进body,Content-Type用application/x-www-form-urlencoded,服务端用express.urlencoded({ extended: true })解析,它的优点是兼容老接口,缺点是嵌套结构表达弱,复杂数据还是走JSON。
前端上传文件时js怎么传二进制数据
文件上传不是把File对象直接丢给服务器就完事,得看接口要的是表单还是裸二进制。
Blob、File、ArrayBuffer怎么选
File:来自<input type="file">或拖拽,本质是带文件名的Blob。Blob:适合直接传一段二进制数据,比如裁剪后的图片。ArrayBuffer:适合需要读取字节、做加密、做分片的场景。
如果接口是multipart/form-data,用FormData包一层File最稳,如果接口明确要application/octet-stream,就把Blob或ArrayBuffer直接放进body:
fetch('/api/raw', {
method: 'POST',
headers: { 'Content-Type': 'application/octet-stream' },
body: arrayBuffer
});
大文件分片上传的实操思路
行业共识认为,大文件上传优先分片,而不是一次性塞进内存,常见做法:
- 用
file.slice(start, end)切出Blob分片。 - 每片单独发请求,带上
chunkIndex、totalChunks、fileHash。 - 服务端合并分片,前端记录已上传分片。
- 失败重试时只补传失败分片。
这套思路能降低单次请求内存占用,也方便做断点续传,注意分片大小要结合业务带宽和服务端限制调整,没有统一标准。
js传输大文件时如何控制带宽成本
成本不只是服务器账单,还包括用户等待时间和失败重传,控制成本可以从三个动作入手。
压缩和分片能省什么
图片在上传前可以压缩,文本JSON可以避免传冗余字段,分片上传能减少一次性失败带来的全量重传,多数情况下,压缩加合理分片比直接传原文件更划算。
缓存和请求头别浪费
静态资源用缓存头,接口参数用ETag或Last-Modified做条件请求,重复提交的场景可以做前端防抖,或者用幂等键让服务端识别重复请求,带宽成本往往就藏在这些重复传输里。
北京前端团队联调时的常见检查点
在北京的前端项目联调中,排查传参问题可以按这个清单走:
- 打开Chrome DevTools的Network面板。
- 点开请求,看Request Headers里的Content-Type。
- 看Payload或Form Data里有没有真实字段。
- 对比服务端日志里的body解析结果。
- 检查网关或Nginx有没有改写请求头。
这个路径能快速定位是前端序列化错,还是服务端解析错。
实操排查:fetch和axios传参容易踩的坑
fetch的Content-Type要手动设
fetch默认不会帮你加JSON请求头,传JSON时记得写headers,传FormData时反而不要写,让浏览器自动带boundary。
axios传FormData别乱设
axios传普通对象默认走JSON,传FormData时直接放body,不要手动设Content-Type,如果项目里封装了拦截器,要确认拦截器没有统一改成application/json。
服务端解析要对应
前端传JSON,服务端要开JSON解析;前端传urlencoded,服务端要开urlencoded解析;前端传文件,服务端要用支持multipart的中间件,前端和后端各说各话,接口就会返回400或空body。
关于js向服务器传数据类型的常见问答
js能直接传Date对象吗?
不能直接传,Date对象经过JSON.stringify会变成ISO字符串,服务端按字符串解析后再转成时间类型,更稳的做法是前端显式传时间戳或格式化字符串,避免时区歧义。
GET请求能传JSON吗?
GET请求的参数在URL上,常规做法是URLSearchParams拼查询字符串,把JSON塞进URL既不好读,也有长度限制,需要传复杂结构时,改用POST更合适。
传二进制一定要用FormData吗?
不一定,接口要表单上传就用FormData;接口要裸二进制就用Blob或ArrayBuffer,并设置对应的Content-Type,选择依据是服务端接口约定,不是前端习惯。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/777137.html


