我转过几个弯 绕过那个小雨楼
拿着蒲扇摆着衣衫渡着紧箍咒
不问天涯不停留 喝过几壶酒
不过年少白头道义放胸口
倘若明天之后 遥看前尘剑封侯
似那天上神仙无所求
朝朝暮暮君如梦醒十分不为何理由
是真是假是惶恐是无休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
又过了几个弯 算尽天量道莫慌
踏这田园闻这芳草香
跌跌撞撞仗剑天涯折煞不枉无笔良
是梦是幻是温柔是家乡
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
谁能与我一醉方休
精华 axios 修改http协议和转formData对象
修改协议
最近发现axios的修改协议有个很方便的操作,就是通过create创建一个axios对象的时候,就可以直接设置上传协议了。
const api = axios.create({
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
});
这样我们将协议改为了application/x-www-form-urlencoded
这个协议可以上传formData对象,但是他不能上传文件,所以如果你要上传文件,需要改为这个协议:multipart/form-data
如果默认配置无法满足,你可以使用官方提供的这种方式改写头信息
api({
url:"xxxx",
data:xxxx,
headers:{
'Content-Type': 'multipart/form-data'
}
})
这样我们就可以覆盖默认的头信息设置了。
转formData对象
formData对象在不同协议下转换的方法是不一样的,这里说两种最常用的请求协议:
纯文本的协议转换方法
比如axios官方默认的:application/json
前端网页form表单的默认协议: application/x-www-form-urlencoded
我百度出两种方法,第一种他虽然能转换成功,但是他会有一个多余的(empty),所以并不完美,所以就不多说了,我这里讲一下比较完美的方案
我们需要一个拦截器,你可以设置一个针对你某个api路径的,也可以全局的,我这里设置一个全局的
import qs from "qs";
api.interceptors.request.use(config => {
config.data = config.data ? qs.stringify(config.data) : null; //转为formdata数据
return config;
}, error => {
console.log(error);
Promise.reject(error);
});
使用该方法转换的formData十分完美,为此我们需要引入一个qs对象,这个对象,你安装了axios他就有的,qs是axios一起带来的,所以不需要单独安装。
传文件的转换
这个就很大的坑了,我之前以为qs是通杀的,所以导致前端传文件,后端总是拿不到,我还以为后端限制文件大小的方法失效了,查了一晚上,头发都薅了不知道多少,无知导致光头,我草了。。。
axios传文件,协议必须是multipart/form-data
如果后端能设置的话,其实也不一定非要formData对象才能传数据,axios里面传{}
对象的形式也是可以的。
但是我用node+koa搭建的时候,使用的是koa-better-body中间件,他传文件只能用formData,否则获取不到文件。
所以必须要转成formData格式
而qs的转换,在multipart/form-data
协议下,他的转换形成的是一个序列化字符串。
我尝试在application/x-www-form-urlencoded
和application/json
协议下输出qs转换的内容,他确实也是一个序列化字符串。
什么是序列化字符串?
实际就是转换成get传参的那种键值对形式的字符串:key=val&key=val&...
所以我推测,axios会根据使用的协议,默认转换这种字符串为formData对象,或者是一个自动转换的功能啥的,不依赖axios,反正使用qs,在之前所说的的两种协议中,是可以正确发送formData对象的。
但是就是multipart/form-data
协议下,即便qs转换了,也是不行的,没有用,哪怕你发送的是纯文本,只要是这个协议,就是不会自动转换为formData。
所以我们需要手动转换,方法也很简单,注意1点就行了。
首先就是不支持FileList对象的直接转换formData对象!!!
不支持FileList对象的直接转换formData对象!!!
不支持FileList对象的直接转换formData对象!!!
重要的事情要说三遍。
文件也分多文件和单文件之分,先挑简单的单文件转换来说
单文件转换
//请求之前拦截器
api.interceptors.request.use(config => {
//单文件转换formData
if (config.headers["Content-Type"] === "multipart/form-data") {
let form = new FormData();
for (let key in config.data) {
form.append(key, config.data[key]);
}
}
return config;
}, error => {
console.log(error);
Promise.reject(error);
});
判断是不是multipart/form-data
协议,是的话就进行formData转换,但是这里就需要注意,不要直接把FileList对象直接丢到要传的参数里面,所以,传参前需要这样处理。
axios.post({
file: file_list[0], //直接传FileList里面的文件
})
file_list等于你input元素change事件的event.target.files
的值,不会傻到照抄把,我这只是简写示例。
这样文件的传输就没问题了。
多文件转换
多文件的转换,为了方便,肯定是直接把FileList对象丢进来了,所以我们的拦截器就需要进行判断了,如果是这个对象的话我们需要进行调整。
这里我们就需要判断这个对象object是不是FileList,那么我们就需要衍生的讲讲另一个知识点:js如何准确判断对象的类型
这里我们拿vue的源码来说一下,非常经典:
var _toString = Object.prototype.toString;
function toRawType(value) {
return _toString.call(value).slice(8, -1)
}
Object.prototype.toString.call([])
返回的是:[object Array]
字符串,不管是什么类型,文字啊,数组啊,布尔啊,等等这些,返回的格式都是这样的:[object xxxxx]
所以使用string的slice方法截取,从第8个字符的左边开始(0位开始算),到最后-1位,也就是最后那个减去1位,刚好把其他的字符都去掉了,留下表示对象格式的文本。
然后我们就可以通过这个进行判断
//请求之前拦截器
api.interceptors.request.use(config => {
//多文件转换formData
if (config.headers["Content-Type"] === "multipart/form-data") {
let form = new FormData();
for (let key in config.data) {
if (Object.prototype.toString.call(config.data[key]).slice(8, -1).toLowerCase() === "filelist") {
config.data[key].forEach(f => {
form.append(key, f);
});
} else {
form.append(key, config.data[key]);
}
}
}
return config;
}, error => {
console.log(error);
Promise.reject(error);
});
formData对同一个key值重复写,他并不会覆盖之前的值,而是进行追加,并且结果类似于arr.push()的效果。
这样一写,你发现对于单文件也是完美兼容的,就是多了几行代码,所以个人更推荐使用这个,当然,对于判断对象类型的,你也可以封装成一个通用方法,然后使用。(vue源码那种写法性能更好一些,我这边省事而已)
以上就是关于转换formData方法的总结了,应该不会有问题了。
评论(0)