ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Axios multipart/form-data 完整指南:FormData 自动序列化、formSerializer 配置与 formToJSON 反向转换

Axios multipart/form-data 完整指南:FormData 自动序列化、formSerializer 配置与 formToJSON 反向转换 Axios multipart/form-data 完整指南FormData 自动序列化、formSerializer 配置与 formToJSON 反向转换【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios本文围绕 Axios 的multipart/form-data发送能力展开从浏览器与 Node.js 环境下的基础提交到 v0.27.0 引入的对象自动序列化机制、formSerializer各配置项dots、metaTokens、indexes、maxDepth等的取值与行为再到 Node.js 下formDataHeaderPolicy头安全策略与formToJSON()的反向解析。读完你可以完整掌握 Axios 表单序列化的规则细节并结合 toFormData.js、setFormDataHeaders.js、formDataToJSON.js 三处源码理解其底层实现。基础提交浏览器与 Node.js 两种写法Axios 支持发送multipart/form-data格式的请求该格式常用于文件上传场景。基本思路是构造一个FormData对象并append数据再将其作为data传给 axios 请求配置。浏览器端直接使用原生FormDataconst formData new FormData(); formData.append(foo, bar); axios.post(https://httpbin.org/post, formData);不要手动设置Content-Type头。对于浏览器、Web Worker 或 React Native 环境中的FormData对象环境本身会自动添加 multipart boundary边界串手动设置反而会破坏请求体格式。在 Node.js 中通常使用社区form-data包Axios 自身的 polyfill 也基于它const FormData require(form-data); const form new FormData(); form.append(my_field, my value); form.append(my_buffer, Buffer.alloc(10)); form.append(my_file, fs.createReadStream(/foo/bar.jpg)); axios.post(https://example.com, form);自动序列化为 FormDatav0.27.0自 v0.27.0 起当请求的Content-Type头被设置为multipart/form-data时Axios 会自动把普通 JavaScript 对象序列化为FormData对象——你无需手工 append 任何字段import axios from axios; axios .post( https://httpbin.org/post, { x: 1 }, { headers: { Content-Type: multipart/form-data, }, } ) .then(({ data }) console.log(data));在 Node.js 版本中默认使用form-datapolyfill你也可以通过配置项env.FormData替换所用的 FormData 类尽管大多数情况下没有必要const axios require(axios); var FormData require(form-data); axios .post( https://httpbin.org/post, { x: 1, buf: Buffer.alloc(10) }, { headers: { Content-Type: multipart/form-data, }, } ) .then(({ data }) console.log(data));从源码看这条“自动序列化”链路位于默认请求转换器 defaults/index.js当data是对象且Content-Type包含multipart/form-data或data本身是 FileList时会调用toFormData()并从this.env.FormData中取出自定义类实例作为容器// lib/defaults/index.js const formSerializer own(this, formSerializer); // ... if ( (isFileList utils.isFileList(data)) || contentType.indexOf(multipart/form-data) -1 ) { const env own(this, env); const _FormData env env.FormData; return toFormData( isFileList ? { files[]: data } : data, _FormData new _FormData(), formSerializer ); }两个值得注意的实现细节其一纯 FileList 会被包装成{ files[]: data }后再序列化因此文件批量上传默认使用files[]字段名其二transformRequest里还有反向判断——如果data已经是FormData而 Content-Type 却是 JSON会走formDataToJSON(data)再JSON.stringify见 defaults/index.js即“FormData JSON 头”会被自动转成 JSON 发送。Node.js FormData 的头拷贝策略 formDataHeaderPolicy仅 Node.js当传入一个暴露了getHeaders()方法的 Node.jsFormData对象如form-data包时Axios 默认会把它返回的全部头拷贝到请求上。这一行为保持了与 v1 的兼容性但当FormData来自不可信来源时存在隐患getHeaders()可能覆盖Authorization等关键头或注入任意自定义头。设置formDataHeaderPolicy: content-only后Axios 只从getHeaders()拷贝Content-Type和Content-Length两个头其余请求头必须通过headers配置显式声明await axios.post(https://example.com/upload, form, { formDataHeaderPolicy: content-only, headers: { Authorization: Bearer my-token, }, });该配置的默认值是legacy全部拷贝。参数解析逻辑非常直白见 setFormDataHeaders.jsconst FORM_DATA_CONTENT_HEADERS [content-type, content-length]; export default function setFormDataHeaders(headers, formHeaders, policy) { if (policy ! content-only) { headers.set(formHeaders); return; } Object.entries(formHeaders || {}).forEach(([key, val]) { if (FORM_DATA_CONTENT_HEADERS.includes(key.toLowerCase())) { headers.set(key, val); } }); }调用点有两处统一在 resolveConfig.js 中基于getHeaders()应用一次Node 的 http 适配器 adapters/http.js 中再次应用以覆盖重定向等场景。更完整的配置说明可参考请求配置文档中的formDataHeaderPolicy小节docs/fr/pages/advanced/request-config.md。特殊终结符{}与[]Axios 的 FormData 序列化器支持两种字段名终结符用于表达特殊序列化操作{}— 将该值用JSON.stringify序列化后作为单个字符串字段发送[]— 把数组类型的对象拆解为多个同名字段发送。注意对数组和FileList类型拆解展开为同名字段是默认行为无需加[]。配置序列化器config.formSerializer序列化器支持通过config.formSerializer配置对象传入额外选项用于处理各种特殊需求选项类型与默认值说明visitorFunction用户自定义的访问器函数会被递归调用以按自定义规则将数据对象序列化为 FormDatadotsboolean false序列化数组和对象时使用点号记法user.name替代方括号记法user[name]metaTokensboolean true在字段名中保留特殊终结符如user{}: {name: John}。后端 body-parser 可利用这些元信息自动按 JSON 解析该值indexesnull \| false \| true false控制为展开后的数组字段追加何种索引null— 不加括号arr: 1、arr: 2、arr: 3false默认— 加空括号arr[]: 1…true— 加真实下标arr[0]: 1、arr[1]: 2、arr[2]: 3maxDepthnumber 100序列化递归的最大对象嵌套深度。超限抛出 code 为ERR_FORM_DATA_DEPTH_EXCEEDED的AxiosError用于防御服务端通过深层嵌套负载发起的 DoS 攻击设为Infinity可关闭限制Blobtypeof Blob将ArrayBuffer值转换为符合规范的FormData时使用的 Blob 构造函数。仅在你的运行时以其他标识符提供了兼容 Blob 构造器时才需要替换例如对确实会超过 100 层嵌套的数据结构放宽限制// 允许更深的嵌套用于确实超过 100 层的数据结构 axios.postForm(/api, data, { formSerializer: { maxDepth: 200 } });安全说明默认 100 层上限是有意为之。服务端把客户端可控的 JSON 透传给 axios 作为data的代码缺少该保护时会面临调用栈溢出风险仅在你的数据结构确实需要时才调高maxDepth。源码视角序列化器内部如何工作核心实现位于 toFormData.js。默认上限与反向转换共享同一个常量保证 FormData 与 JSON 互转的深度语义对称toFormData.js// lib/helpers/toFormData.js export const DEFAULT_FORM_DATA_MAX_DEPTH 100;几个关键机制深度保护build()在每次递归前调用throwIfMaxDepthExceeded(depth)检查层级超限即抛出带ERR_FORM_DATA_DEPTH_EXCEEDEDcode 的AxiosErrortoFormData.js对{}终结符触发的JSON.stringify也内置了等价的深度限制stringifyWithDepthLimit()。循环引用检测build()维护一个stack遇到重复对象立即抛出Circular reference detected错误避免死递归toFormData.js。indexes三分支平铺数组展开时的字段名生成就写死在defaultVisitor中——indexes true走renderKey([key], index, dots)生成arr[0]indexes null直接用key其余情况默认false拼key []toFormData.js。metaTokens{}终结符在metaTokens为true时原样保留在字段名里否则被key.slice(0, -2)剥掉toFormData.js。dots字段路径渲染由renderKey(path, key, dots)完成dots为真时用.连接否则用方括号包裹toFormData.js。值类型转换Date转为 ISO 字符串、布尔转字符串、ArrayBuffer/TypedArray 在有规范兼容Blob时转 Blob、在 Node 有 Buffer 时转 BufferconvertValuetoFormData.js。visitor扩展点自定义 visitor 以 FormData 为this被调用返回true表示继续递归遍历该值同时可访问defaultVisitor、convertValue、isVisitable及一组is*类型判断辅助函数exposedHelperstoFormData.js。序列化过程完整示例以一个典型的嵌套对象为例const obj { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: Peter, surname: Griffin }, { name: Thomas, surname: Anderson }, ], obj2{}: [{ x: 1 }], };Axios 序列化器内部等价于执行了以下步骤const formData new FormData(); formData.append(x, 1); formData.append(arr[], 1); formData.append(arr[], 2); formData.append(arr[], 3); formData.append(arr2[0], 1); formData.append(arr2[1][0], 2); formData.append(arr2[2], 3); formData.append(users[0][name], Peter); formData.append(users[0][surname], Griffin); formData.append(users[1][name], Thomas); formData.append(users[1][surname], Anderson); formData.append(obj2{}, [{x:1}]);规则可以归纳为纯标量直接追加无嵌套的平铺数组默认展开为key[]同名多值混合数组按真实下标生成路径arr2[1][0]对象数组递归展开为users[0][name]这类路径{}终结符的值整体JSON.stringify成单字段。反向转换axios.formToJSON()axios.formToJSON()能把字段名中的点号与方括号记法还原为嵌套的对象和数组结构。只有.、[、]是结构性分隔符其余字符如-、空格、、*、都会原样保留在字面量键中const form new FormData(); form.append(user-name, johndoe); form.append(user.name, john); console.log(axios.formToJSON(form)); // { // user-name: johndoe, // user: { name: john } // }user[name]同样会产生嵌套对象路径items[]则产生数组。实现见 formDataToJSON.js几个实现要点路径解析使用正则/[^.[\]]|\[([^.[\]]*)]/g把foo[x][y][z]拆为[foo,x,y,z]foo.x.y.z同理段内不含[的设计让解析保持线性复杂度formDataToJSON.js。__proto__段被直接跳过杜绝原型污染同名重复字段会合并为数组formDataToJSON.js。数字键0、1…在目标位置自动形成数组非数字键还原为对象属性解析同样受 100 层深度上限保护超限抛ERR_FORM_DATA_DEPTH_EXCEEDED。该函数挂在 axios 实例上且支持直接传 HTMLform元素——内部会先new FormData(thing)再解析axios.js。对应的单元测试见 tests/unit/helpers/formDataToJSON.test.js。toFormData本身也作为axios.toFormData暴露axios.js可以脱离请求流程手动序列化对象测试用例见 tests/unit/toFormData.test.js。便捷方法postForm / putForm / patchFormAxios 还提供三个快捷方法postForm、putForm、patchForm。它们与对应的 HTTP 方法完全等价唯一区别是预先将Content-Type头设置为multipart/form-data因此可以直接传普通对象// 等价于 axios.post(/api, data, { headers: { Content-Type: multipart/form-data } }) axios.postForm(/api, { name: john, file: new Blob([...]) });这也意味着postForm系列方法天然触发前文描述的自动序列化流程并受同一套formSerializer选项约束。小结浏览器 / Web Worker / React Native用原生FormData直接传data不要手动设置Content-Typeboundary 由环境生成。Node.js使用form-data包或依赖自动序列化对不可信来源的 FormData用formDataHeaderPolicy: content-only收敛头拷贝范围。对象自动序列化要求Content-Type含multipart/form-dataformSerializer的visitor/dots/metaTokens/indexes/maxDepth/Blob覆盖几乎全部特殊场景maxDepth默认 100 层是服务端 DoS 防护线。终结符{}JSON 化与[]字段展开是字段级的序列化开关formToJSON()按.、[、]三个结构性分隔符做无损逆向解析并与序列化器共享同一深度上限。【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表