前言
之前在用vue写项目的时候对axios做了一层封装,写了一篇文章 vue中用axios post方式报错的解决方法;最近在做一个react的项目时与同事交流的过程中发现之前那样的做法并不好,所以,现在要对之前的做法做一些改进。
改进点
1、主要是在axios上进行扩展,而不是修改axios本身。
2、新加post 的content-type: application/json的提交方式;
3、对form表单提交做了扩展,支持普通对象参数与及formData对象,(formData对象适于ajax上传图片)
新建一个ajax.js
import axios from 'axios'
import qs from 'qs'
const TIME_OUT = 30000 // 超时时间30秒
// 请求数据拦截处理
axios.interceptors.request.use(config => {
// code... 你的逻辑
return config
}, error => {
return Promise.reject(error)
})
// 返回数据拦截处理
axios.interceptors.response.use(response => {
//code... 你的逻辑
return response.data //直接返回后台返回的json object
}, error => Promise.reject(error.response))
/*
* 封装一个私有的请求方法
*/
const _request = (method, url, data) => {
const headers = {}
const configData = {
url, // 请求的地址
timeout: TIME_OUT, // 超时时间, 单位毫秒
headers
}
if (method === 'get') {
configData.method = 'get'
configData.params = data// get 请求的数据
} else if (method === 'postForm') {
configData.method = 'post'
if (Data instanceof FormData) {
configData.headers['Content-Type'] = 'multipart/form-data; charset=UTF-8'
configData.data = data
} else {
configData.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
configData.data = qs.stringify(data)
}
} else if (method === 'postJson') {
configData.method = 'post'
configData.headers['Content-Type'] = 'application/json; charset=UTF-8'
configData.data = data
}
return axios(configData)
}
class Ajax {
get = (url, data = {}) => {
return _request('get', url, data)
}
postForm = (url, data = {}) => {
return _request('postForm', url, data)
}
postJson = (url, data = {}) => {
return _request('postJson', url, data)
}
post = this.postJson // 默认post的Content-Type是application/json
}
export default new Ajax()
上面的代码首先 封装一个私有的请求方法(_request),由于开发前后端联调期间,肯定是跨域的,我们就采用cors方法,并且我们的后端在接口中要用到cookie,所以必须设置withCredentials: true;然后再 建一个Ajax class class里面定义了项目需要的请求方法。主要有三个:
get,
postJson,
postForm,
默认post === postJson,我们的post请求默认就是 'Content-Type: application/json; charset=UTF-8'的,与后端的联调时也踩了几个坑,
主要是后端没有配合对应的response header:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin: 'localhost:3000'
Access-Control-Allow-Headers: 'content-type'
在response headers里面必须要有上面三个字段,第一个是允许跨域cookie的,第二个就是你的本地开发服务的域名端口,后端同学可以在request headers的origin字段里面拿,Access-Control-Allow-Origin不可以为'*',因为 '*' 会和 Access-Control-Allow-Credentials:true 冲突,需配置指定的地址;最重要就是第三个Access-Control-Allow-Headers: 'content-type',因为我们的项目的post请求默认是用了content-type:application/json形式的,而在跨域的情况下,如果前端设置的request headers的content-type里面如果不是以下三个值之一
application/x-www-form-urlencoded
multipart/form-data
text/plain
浏览器就会在每次ajax请求时都会发一个基于options方式的预检请求去询问服务端是否接收请求,这时候Access-Control-Allow-Headers: 'content-type'就起到作用了,不然预检请求都不通过,更不用说预检请求之后的正式请求了。
用法
定义好ajax之后就要用了:
//在你要发请求的地方引入
import Ajax from './ajax'
const url = 'xxx.com/xxx/xxx'
const data = {key1: 'value1',key2: 'value2'}
// get
Ajax.get(url,data).then(res => {
// 你的逻辑
}).catch(err => {
// 你的逻辑
})
// post with content-type:application/json
Ajax.post(url,data).then(res => {
// 你的逻辑
}).catch(err => {
// 你的逻辑
})
// or
Ajax.postJson(url,data).then(res => {
// 你的逻辑
}).catch(err => {
// 你的逻辑
})
// post with content-type:application/x-www-form-urlencoded
Ajax.postForm(url,data).then(res => {
// 你的逻辑
}).catch(err => {
// 你的逻辑
})
// post with formData,
// 如上传图片
const formData = new FormData()
formData.append('file', this.file) // this.file是input type='file'选中的图片
Ajax.postForm(url,formData).then(res => {
// 你的逻辑
}).catch(err => {
// 你的逻辑
})
经过这次修改之后,以后在每个项目中都可以这样用了。
如果在vue中你还可以直接把挂载在vue.prototype上,就不用每次都去导入,
// export default new Ajax()
Vue.prototype.$ajax = new Ajax()
// 用到就直接this.$ajax.get()
this.$ajax.get(url,data).then(res => {
// 你的逻辑
}).catch(err => {
// 你的逻辑
})
甚至你可以做成一个插件的形式,在ajax.js中最后导出一个函数
// ajax.js
export default (Vue) => {
if (typeof window !== 'undefined' && window.Vue) {
Vue = window.Vue
}
Vue.prototype.$ajax = new Ajax()
}
然后在main.js中这么写
import Vue from 'vue'
import Ajax from './ajax'
Vue.use(Ajax)