浏览器原生表单提交默认使用 application/x-www-form-urlencoded 或 multipart/form-data 编码,不支持直接发送 json 字符串;必须用 fetch 或 xmlhttprequest 显式设置 content-type: application/json 并调用 json.stringify() 发送,且需阻止默认提交行为。

为什么直接用 form.submit() 无法提交 JSON 数据
浏览器原生表单提交(submit 事件或 form.submit())默认把数据编码为 application/x-www-form-urlencoded 或 multipart/form-data,根本不会发送原始 JSON 字符串。即使你手动 JSON.stringify() 了数据,不改请求头、不用 fetch 或 XMLHttpRequest,服务器收到的只是乱码或空体。
常见错误现象:req.body 在 Express 中为空、$_POST 在 PHP 中收不到字段、后端日志显示 Content-Length: 0 或解析失败。
- 必须显式设置
Content-Type: application/json - 必须用
fetch()或XMLHttpRequest发送,不能依赖<form></form>的原生提交行为 - 若仍想保留
<form></form>结构,需在submit事件中event.preventDefault()
用 fetch() 提交 JSON 表单最简写法
这是目前最推荐的方式:简洁、可读性强、天然支持 Promise。关键点不在“怎么发”,而在“怎么组织数据”和“怎么处理响应”。
示例场景:一个登录表单 <form id="loginForm">
<input name="email"><input name="password">
</form>
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const json = Object.fromEntries(formData.entries());
try {
const res = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(json)
});
const data = await res.json();
if (res.ok) console.log('登录成功', data);
} catch (err) {
console.error('请求失败', err);
}
});
-
FormData.entries()只取name属性存在的字段,忽略无名控件 - 如果表单含
<input type="checkbox" checked>,FormData默认只传值,不传true/false—— 需手动补逻辑 - 注意:
Object.fromEntries()在 IE 中不支持,需 polyfill 或改用reduce
遇到 400 Bad Request 或后端解析失败怎么办
这类错误几乎都出在请求体与后端期待格式不一致。不是前端没发,而是发得“不对味”。
- 检查后端是否真的配置了 JSON 解析中间件(如 Express 需
app.use(express.json());Fastify 默认开启,但需确认contentType匹配) - 确认
Content-Type头严格等于application/json(多一个空格、分号或字符都会被拒绝) - 用浏览器 DevTools 的 Network → Payload 查看实际发出的字符串 —— 是否是合法 JSON?有无多余逗号、单引号、未转义换行?
- 如果后端用 Python Flask,注意它默认不解析 JSON body,要主动调用
request.get_json(),且Content-Type必须匹配
要不要用 XMLHttpRequest 替代 fetch
除非要兼容 IE11 或需要精细控制上传进度,否则没必要。但真要用,核心差异只有三点:
-
XMLHttpRequest必须手动调用xhr.setRequestHeader(),fetch直接写在headers对象里 -
xhr.responseType = 'json'才能让xhr.response自动解析,而fetch必须显式调用.json() -
XMLHttpRequest的错误捕获更琐碎:网络失败、超时、状态码异常都要分开监听(onerror/ontimeout/onload),fetch抛错只覆盖网络层,4xx/5xx 仍进then
真正容易被忽略的是:无论用哪种方式,表单中 <select multiple></select> 的值、文件输入(<input type="file">)都无法直接进 JSON —— 它们需要额外处理(如转成 base64 或走 FormData + multipart)。JSON 表单只适合纯文本/数值字段。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











