0%

请求方式的封装

2019年终于接触了es6的一些语法,学习了有关比较有意思的一些语法,先记录下来,以供自己时时回忆把玩。
大致以现在比较流行的几个框架进行书写。
以VUE为例子:

中间涉及到promise的异步回调,以及async和await的使用。因为VUE没有自带请求方法,所以引入axios插件

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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import axios from 'axios'
import Vue from 'vue'
import { ACCESS_TOKEN, REFRESH_TOKEN } from '../store/mutation-types'
import { notification } from 'ant-design-vue/es'
import store from '../store'

const domains = {
url: {
manager: 'http://XXX:8080',
},
}

const currentDomain = domains.url

const AjaxHandler = {
domains,
currentDomain
}

// ----------------------- all ths about AjaxHandler.fetch begin----------------------- //
AjaxHandler.fetch = (resource, body, options) => {
// const token = getToken()
const accessToken = Vue.ls.get(ACCESS_TOKEN)
const refreshToken = Vue.ls.get(REFRESH_TOKEN)
let url = ''
let hdrs = {}
// --------- normal POST request ---------
if (!options) {
url = currentDomain.manager + resource
hdrs = {
'Content-Type': 'application/json',
Accept: 'application/json' || '*/*',
accessToken: accessToken,
refreshToken: refreshToken
}
}
// console.log("===", options)
if (options && options.port) {
url = currentDomain[options.port] + resource
hdrs = {
'Content-Type': 'application/json',
Accept: 'application/json' || '*/*',
accessToken: accessToken,
refreshToken: refreshToken
}
}

// --------- oss request ---------
if (options && options.oss) {
url = options.resource
hdrs = {
mode: 'cors'
}
}
const fetch_promise = new Promise((resolve, reject) => {
axios
.post(url, body, { headers: hdrs })
.then(res => resolve(res))
.catch(err => reject(err))
})
return abortivePromiseWrapper(fetch_promise)
}

const abortivePromiseWrapper = fetch_promise => {
let timeoutAction = null
// 这是一个可以被reject的promise
const timerPromise = new Promise((resolve, reject) => {
timeoutAction = () => {
reject('timeout')
}
})
// 这里使用Promise.race,以最快 resolve 或 reject 的结果来传入后续绑定的回调
const abortive_promise = Promise.race([fetch_promise, timerPromise])
.then(statusChecker)
.then(contentTypeChecker)
.then(res => serviceHandler(res))
.catch(err => errorCatcher(err))

/* reject timeout promise after 60s */
setTimeout(() => {
timeoutAction()
}, 1*60*1000)

return abortive_promise
}

const statusChecker = response => {
if (response.status >= 200 && response.status < 300) {
return response
}
throw response
}

const contentTypeChecker = response => {
let contentType = response.headers['content-type']
let accessToken = response.headers['accessToken']
let refreshToken = response.headers['refreshToken']
let contentLength = response.headers['content-length']

if (accessToken && refreshToken) {
Vue.ls.set('ACCESS_TOKEN', accessToken)
Vue.ls.set('REFRESH_TOKEN', refreshToken)
}
// 常规json格式
if (contentType && contentType.includes('application/json')) {
return response.data
}
// oss图片
if (contentLength === '0') {
return response
}
// 兼容下载excel[react用fetch实现导出Excel表](https://blog.csdn.net/Tanganq/article/details/88891644)
if (contentType && contentType.includes('application/vnd.ms-excel;charset=utf-8')) {
return response.blob().then(blob => {
const contentDisposition = response.headers['content-disposition']

let url = window.URL.createObjectURL(blob)
let a = document.createElement('a')
a.href = url
const fileName = contentDisposition ? contentDisposition.split('=')[1] : 'xlFilename.xls'
a.download = decodeURIComponent(fileName)
a.click()
})
}
throw { result: false, msg: '返回的数据类型出错' }
}

const serviceHandler = json => {
if (!json) return
// 兼容图片上传
if (json.status === 204) {
return json
}
// 接口内容逻辑判断
if (!json.result) {
throw json
} else {
return json
}
}

const errorCatcher = error => {
if (!error) return
// 1.前端自行判断先判断超时、数据类型错误
if (error === 'timeout') {
throw { result: false, msg: '请求超时,请稍后再试' }
}
// 2.判断网络错误
if (error && error.response) {
const status = error.response.status || ''
switch (status) {
case 401:
hintExpire()
break
case 403:
hintForbidden()
break
case 400:
break
default:
break
}
throw error.response.data || ''
}
// 3.判断业务逻辑错误
if (!error.result) {
throw error
}
}

// ----------------------- all ths about AjaxHandler.fetch end----------------------- //

// 令牌过期
const hintExpire = () => {
notification.error({
message: '您的账户令牌已过期,或已在别的地方登录',
description: '您将被强制退出,请重新登录',
duration: 0,
key: 'expire',
btn: h => {
return h(
'a-button',
{
props: {
type: 'danger',
size: 'small'
},
on: {
click: () => {
store.dispatch('basic/Logout').then(() => {
window.location.assign('/')
})
}
}
},
'确认'
)
}
})
}
// 令牌被禁止访问
const hintForbidden = () => {
notification.info({
message: 'Forbidden',
description: '账号权限被拒绝',
key: 'expire',
duration: 0
})
}

export default AjaxHandler