❄️

碰了一个formdata的壁

问题

之前有个登录接口, 在手机上使用fetch+multipart/formdata没出现过任何问题, 但是使用此接口在开发react项目时候, 后台就会报参数取不到.

const _d  = new FormData(); _d.append('mobile', '17511111111'); _d.append('verifycode', '1234');
const loginUrl = 'https://****.com/api/v1/login';

// with x-www-form-urlencoded
const formUrlencodedHeader = { 'Content-Type': 'application/x-www-form-urlencoded' };
fetch(loginUrl, {method: 'POST', headers: formUrlencodedHeader, body: _d}).then(r => r.json()).then(r=>console.log(r));
// 参数取不到

// with multipart/form-data
const multipartHeader = { 'Content-Type': 'multipart/form-data' };
fetch(loginUrl, {method: 'POST', headers: multipartHeader, body: _d}).then(r => r.json()).then(r=>console.log(r));
// 参数取不到

// with default header
fetch(loginUrl, {method: 'POST', body: _d}).then(r => r.json()).then(r=>console.log(r));
// 登录成功

解析

application/x-www-form-urlencoded 会将表单内的数据转换为键值对,比如`name=java&age=23 . 他是默认的MIME内容编码类型,一般可以用于所有的情况。但是他在传输比较大的二进制或者文本数据时效率极低。

这种情况应该使用”multipart/form-data”。如上传文件或者二进制数据和非ASCII数据。multipart/form-data 会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。每条数据由 boundary 隔离,所以 multipart/form-data 既可以上传文件,也可以上传键值对,它采用了键值对的方式,所以可以上传多个文件。

当使用RN的 fetch 进行网络请求, 虽然写的Content-Typemultipart/form-data, 但系统还会将 boundary 添加到后面:

但是使用前端的 fetch 进行网路请求时候, 执行环境是浏览器, 浏览器并不会替换你的 Content-Type 字段, 所以默认的 multipart/form-data; boundary=-R-blabla.. 被你的 headers 给替换掉了, 后台得不到 boundary 字段, 无法分割 form-data.

当指定为 application/x-www-form-urlencoded 时, 由于此格式k=v&k2=v2形式, 但是发送的 formdata 没有指定 boundary, 故后台也无法解析.

默认情况, 浏览器会自动加上 boundary, 并且浏览器优化好样式.

所以, 使用 formdata 不要写 Content-Type.

参考


在我们一生中,命运赐予我们每个人三个导师,三个朋友,三名敌人,三个挚爱。但这十二人总是不以真面目示人,总要等到我们爱上他们、离开他们、或与他们对抗时,才能知道他们是其中哪种角色。