一般需求
axios
对外暴露了基本的 6
种请求方式,对付一般的需求差不多足够了:
- axios.delete()
- axios.get()
- axios.head()
- axios.post()
- axios.put()
- axios.patch()
当然,如果我们需要更改或添加请求头信息的话,那就不能使用这已经包装好的 6
个方法了。
需要这样使用:
axios({
method:'POST/GET/PUT...',
url:'...',
data:'body data',
params:'query string',
headers:{
/* 需要更改/添加的请求头 */
}
})
中级需求
上面的这几种方式,并没有过滤请求和响应,所以如果把 axios
作为项目中需要使用的 ajax
请求库的话,改造还需要继续下去。
过滤请求和响应
- 基于
Promise
的过滤
// 过滤请求
axios.interceptors.request.use((config) => {
/* 发送请求前可以对 config 中的内容进行改造
比如添加头信息,特定请求不允许通过等等 */
/* 方式一:直接返回处理后的 config */
return config
/* 方式二:返回一个 Promise 对象 */
return new Promise((resolve,reject) =>{
// do something
// resolve 的参数即为处理完的 config
// reject 调用会终止请求的调用
resolve(config) / reject(err)
})
})
// 过滤响应
axios.interceptors.response.use(result => {
/* result 为服务器响应回来的数据 */
/* 方式一:直接返回处理后的 result */
return result
/* 方式二:返回一个 Promise 对象 可以根据服务端返回的错误码进行判断 */
if(result.data.code != 0){
return Promise.reject(result)
}
},err => {
/* 当响应出错的时候 */
return Promise.reject(result)
})
这种过滤方式是在请求的两端分别加上了一串的 Promise
先执行 interceptors.request
中注册的处理函数然后发送请求,最后执行 interceptors.response
中注册的函数。
interceptors.request
中的处理函数中的参数为调用 axios
传入的 config
。
interceptors.response
中的处理函数中的参数为服务器返回的结果。
- 基于配置的过滤
由于 axios
足够的灵活,过滤响应请求不仅仅是上面这种方式,但是不太建议使用以下方式进行过滤,因为这会改变默认的行为。
在配置 config
对象时,我们说过在该对象下,有这么两个属性 transformRequest
transformResponse
具体见该篇文章 axios - 2 - 参数字段。
所以在配置 config
对象时,我们只需要改变这两项对应的数组就可以了
{
url: '/user',
// 请求方式
method: 'get', // default
// 用于改变请求的函数数组
transformRequest: [function (data, header) {
// 改变请求内容
// 这里不支持异步的调用,仅仅使用于同步,所以更改 header 的内容必须同步进行
// 不用返回 header 返回 data 即可
return data;
}],
// 用于改变响应内容的函数数组
transformResponse: [function (data) {
// 改变响应内容
// 同样不支持异步操作
return data;
}],
// 头信息
headers: {'X-Requested-With': 'XMLHttpRequest'},
}
注:之所以不推荐大家这么写的原因在于这两个有默认值,在 defaults 中,有这么一段:
{
transformRequest: [function transformRequest(data, headers) {
normalizeHeaderName(headers, 'Content-Type');
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
utils.isBlob(data)
) {
return data;
}
if (utils.isArrayBufferView(data)) {
return data.buffer;
}
if (utils.isURLSearchParams(data)) {
setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
return data.toString();
}
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}
return data;
}],
transformResponse: [function transformResponse(data) {
/*eslint no-param-reassign:0*/
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore */ }
}
return data;
}],
}
所以当你在 config
中添加了这两个属性时,会将默认给替换掉,所以你会很悲剧的发现为啥我给的参数都没有过去,响应回来的竟然需要我手动 JSON.parse
一下,当然还是有解决办法的,由于是数组,所以只需要把这两个默认的给带上就可以了,但不得不说这很麻烦,不是吗?
高级需求
当你准备把 axios
这个库作为你项目的底层库时,你不得不考虑方法的通用以及一致性时,就会有一下几点思考:
- 在不改变
axios
这个库的默认行为的情况下(也就是不改变原有的axios
的行为,生成一个新的axios
),去做一些请求的封装。 - 基本上所有的请求都是同一域名,当服务器迁移的时候,如何能更便捷的去修改请求,以适应当前的服务器环境。
- 一些必要的请求头信息是所有请求都要附带的,比如
token
auth
等等。 - 服务器返回的是有规则的响应,如何统一过滤掉,而不必每个请求单独写。
- 这个新生成的库与调用时应该是无影响的,也就是说可以足够的可配置。
所以在考虑了这些之后,便有了以下文件
import axios from 'axios'
/* 创建一个新的 axios 对象,确保原有的对象不变 */
let axiosWrap = axios.create({
baseURL: /* 服务器的根路径 */,
headers: {
/* 一些公用的 header */
'token': appInfo.token
},
transformRequest:[function (data, header){
/* 自定义请求参数解析方式(如果必要的话) */
}],
paramsSerializer:function(params){
/* 自定义链接参数解析方式(如果必要的话) */
}
})
/* 过滤请求 */
axiosWrap.interceptors.request.use((config) => {
return config
})
/* 过滤响应 */
axiosWrap.interceptors.response.use((result) => {
/* 假设当code为0时代表响应成功 */
if (result.data.code != 0) {
return Promise.reject(result)
}
return result.data.data
}, result => {
return Promise.reject(result)
})
export default axiosWrap
虽然挺简单的,但很实用,不是吗?