修改协议

最近发现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-urlencodedapplication/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方法的总结了,应该不会有问题了。

分类: vue 项目实战 标签: vueFormDataaxios修改http协议转换formData对象

评论

暂无评论数据

暂无评论数据

目录