使用AJAX的方式发送FORM表单数据
详细介绍前端使用 AJAX 发送 FORM 表单数据的问题与解决方案,核心围绕中文乱码及 axios 设置请求头失效展开。
介绍 form 标签enctype属性及multipart/form-data的 MIME 规范,给出自定义 AJAX 方法:手动组织 FormData、设置带编码和分隔符的请求头、拼接请求体并以二进制发送。
问题缘由
在前端(vue,axios)提交表单数据到后端(springboot)时,表单字段如果存在中文,后端接收到数据会解析为乱码。这篇文章https://blog.csdn.net/weixin_42161320/article/details/129283121给出的原因是,后端接收到数据时默认的编码格式为iso_8859_1。
问题的缘由为前后端编码的问题所致,后端设置编码可以修正该问题。通过postman调用接口测试,前端指定参数编码也可以解决该问题。解决方式为在表单提交过程中,手动指定请求头Content-Type值为multipart/form-data; charset=UTF-8即可。
axios无法设置表单请求头
尝试修改axois请求,在发送请求前,手动设置请求头。代码如下,实际测试时发现,该方式并不会生效。
1 | let formData = new FormData(); |
在axios0.xx.xx版本,0.27.2、0.26.0,设置的请求头会被忽略。在1.xx.xx版本,Content-Type会被修改为false。经过查询资料,在原生浏览器实现中,form表单提交时,由于form-data存在特殊的格式,当不设置Content-Type或者设置Content-Type为false时,浏览器默认会设置这个请求头。在浏览器默认设置的请求头中,并不会设置chartset编码。
form标签规范
HTML的form标签用来提交表单数据,文件上传通常以表单的形式提交。
在form属性中,enctype 属性用来设置表单的数据类型,当 method 属性值为 post 时,将表单的内容提交给服务器的 。可能的取值有:
application/x-www-form-urlencoded:未指定属性时的默认值。multipart/form-data:当表单包含type=file的<input>元素时使用此值。text/plain:出现于 HTML5,用于调试。这个值可被<button>、<input type="submit">或<input type="image">元素上的formenctype属性覆盖。
当请求方式不为post时,该属性enctype无效。
MIME规范:multipart/form-data
作为重要的MIME类型,MDN对multipart/form-data编码规范进行了说明, 作为多部分文档格式,它由边界线(一个由'--'开始的字符串)划分出的不同部分组成。每一部分有自己的实体,以及自己的 HTTP 请求头。 Content-Disposition和 Content-Type 用于文件上传领域。
请求头以及提交的请求头格式,形如:
1 | # 请求头 |
上述格式中给出了一个表单中,包含一个文件和一个其他字段的格式,每个字段之间通过--aBoundaryString进行分割,字段说明信息和字段值之间存在一个空行,文件字段比普通字段,多出filename和Content-Type信息。
如果有更多的字段,追加在下方即可。
所以,在上例ajax给出的表单请求,发送请求长这样:
1 | # 请求头 |
通过ajax的方式发送form-data
既然浏览器默认的方式无法指定请求头Content-Type值为multipart/form-data; charset=UTF-8,在了解浏览器表单提交数据的格式后,可以通过ajax的方式进行模拟。
在MDN文档使用 XMLHttpRequest中,给出了一个小框架,用于模拟表单提交的示例, 在使用 JavaScript 做表单提交的工作时,你需要完全模拟浏览器表单提交的行为。
在简化这个示例过程中,将表单提交的过程简化为一下几个步骤:
- 组织formData对象
- 手动设置请求头
Content-Type值为multipart/form-data; charset=UTF-8; boundary=分隔符 - 手动拼接请求体格式
- 将请求体作为二级制数据,使用xhr发送请求
1 | /** |
使用该示例也较为简单
1 | let formData = new FormData(); |
限制
- 该框架使用
FileReaderAPI 进行文件的上传。这是一个较新的 API 并且还未在 IE9 及以下版本的浏览器中实现。因此,使用 AJAX 上传仍是一项实验性的技术。如果你不需要上传 二进制文件,该框架在大多数浏览器中运行良好。 - 发送二进制内容的最佳途径是通过
ArrayBuffers或Blobs结合send()方法甚至FileReaderAPI 的readAsArrayBuffer()方法。但是,自从该脚本的目的变成处理 可字符串化 的原始数据以来,我们使用sendAsBinary()方法结合FileReaderAPI 的readAsBinaryString()方法。同样地,上述脚本仅当你处理小文件时行之有效。如果不打算上传二进制内容,就考虑使用FormDataAPI 来替代。 - 非标准的
sendAsBinary方法从 Gecko 31 开始将会废弃并且会很快被移除。标准方法send(Blob data)将会取而代之。
FormData 对象的使用
FormData 对象用以将数据编译成键值对,以便用XMLHttpRequest来发送数据。其主要用于发送表单数据,但亦可用于发送带键数据 (keyed data),而独立于表单使用。如果表单enctype属性设为 multipart/form-data,则会使用表单的submit()方法来发送数据,从而,发送数据具有同样形式。
你可以自己创建一个FormData对象,然后调用它的append()方法来添加字段,像这样:
1 | var formData = new FormData(); |
参考
使用AJAX的方式发送FORM表单数据

