对 Axios 进行简单封装

一、概述

Axios 进行简单封装。

二、目标

1、异常处理

对于将网络错误、HTTP 错误和返回值错误合并,方便上层处理。
服务端返回的数据至少包含 codemessage 字段。code200 时为正常返回。

2、支持跳转 URL

服务端返回的数据如果包含 url 字段,则自动跳转至对应地址。

备注:如果包含 url 字段,不会对 code 进行判断。

3、JSON 序列化

对于 postputpatch 方法提交的数据,序列化成 JSON 。

4、QueryString 序列化

对于 getdeletehead 方法提交的数据,序列化成 QueryString 。

5、同时支持 JOSN 和 文件下载

服务器可能返回不同的数据格式,比如返回文件或 JSON 格式————当文件由于某些原因不可下载时返回 JSON 格式。

6、封装 Vue plugin

Axios 的实例封装成一个 plugin ,方便 Vue.use(xxxx) 。

三、实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import axios from 'axios'
import qs from 'qs'
import { baseURL } from './config.js'

const SuccessCode = 200

// 自定义错误
const ErrorType = {
'Default': 'default',
'Sysetem': 'sysetem'
}

function ApiError (message, errorType, innerError) {
this.name = 'ApiError'
this.message = message || 'Default Message'
this.errorType = errorType || ErrorType.Default
this.innerError = innerError
this.stack = (new Error()).stack
}
ApiError.prototype = Object.create(Error.prototype)
ApiError.prototype.constructor = ApiError

const httpClient = axios.create({
baseURL: baseURL,
timeout: 20000,
// 响应的数据格式 json / blob / document / arraybuffer / text / stream
responseType: 'json',
withCredentials: true
})

// POST传参序列化(添加请求拦截器)
httpClient.interceptors.request.use(
config => {
// 在发送请求之前做某件事
if (
config.method === 'post' ||
config.method === 'put' ||
config.method === 'patch'
) {
// Content-Type 对于 POST、PUT 和 PATCH 才有意义
config.headers = {
// 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
'Content-Type': 'application/json; charset=UTF-8'
}
// 序列化
config.data = JSON.stringify(config.data)
} else if (
config.method === 'delete' ||
config.method === 'get' ||
config.method === 'head'
) {
// QueryString 序列化器
config.paramsSerializer = function (params) {
// arrayFormat: indices brackets repeat
return qs.stringify(params, {
arrayFormat: 'indices'
})
}
}

// 若是有做鉴权token , 就给头部带上token
if (localStorage.token) {
config.headers.Authorization = localStorage.token
}
return config
},
error => {
return error
}
)

// 返回状态判断(添加响应拦截器)
httpClient.interceptors.response.use(
response => {
if (response.headers['content-type'].indexOf('json') === -1) {
// 返回的数据不是 json (或是 json 但服务器的 content-type 设置不准确)
return response
}
// 仅处理 json 数据
let json
if (response.request.responseType === 'arraybuffer' && response.data.toString() === '[object ArrayBuffer]') {
// 返回的数据是 arraybuffer,内容是 json
// 备注:可能内容不是 json,这里暂未处理
const text = Buffer.from(response.data).toString('utf8')
console.log(text)
json = JSON.parse(text)
} else {
// 备注:不是 arraybuffer 可能会是 blob 等,这里暂未处理
json = response.data
}

if (json && json.url) {
top.location = json.url
} else if (json && json.code !== SuccessCode) {
console.log(json)
return Promise.reject(new ApiError(json.message))
}
return response
},
error => {
// 返回 response 里的错误信息
return Promise.reject(new ApiError(error.message, ErrorType.Sysetem, error))
}
)

// 将 Axios 的实例封装成一个 plugin ,方便 Vue.use(xxxx)
export default {
install: function (Vue, option = {}) {
// 1.通过 Vue.httpClient 调用
Vue.httpClient = httpClient
// 2.通过 this.$httpClient 调用
Vue.prototype.$httpClient = httpClient
}
}

config.js 内容:

1
export const baseURL = 'http://www.yourdomain.com/api'

四、使用

1、Vue 配置

1
2
3
4
5
6
7
8
// ......
import Vue from 'vue'
import HttpClient from '@/utils/httpclient.js'

import App from './App.vue'

Vue.use(HttpClient)
// ......

2、示例

1
2
3
4
5
6
7
8
9
10
11
12
login () {
const params = {
username: 'test',
password: '123456'
}
Vue.httpClient.post('/login', params).then(response => {
console.log(response.data)
}, error => {
// error 为 ApiError 实例。对于网络错误或 HTTP 错误等系统错误,可通过 innerError 查询实际错误。
console.log(error)
})
}

3、下载支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const fileDownload = require('js-file-download')

testDownload () {
const url = 'http://www.yourdomain.com/somefile.zip'
const params = {
token: '35dea0e5-9b9f-4b8b-981e-2e1120b1c317'
}
Vue.httpClient.post(url, params, {
// 重要
responseType: 'arraybuffer'
}).then(response => {
fileDownload(response.data, 'somefile.zip')
}, error => {
console.log(error)
})
}

参考资料

https://juejin.im/post/59a22e71518825242c422604
https://developer.mozilla.org/zh-CN/docs/Web/Guide/AJAX