Skip to content

Commit aa591a5

Browse files
committed
添加 '31-axios封装上'
1 parent 50a4e55 commit aa591a5

File tree

1 file changed

+329
-0
lines changed

1 file changed

+329
-0
lines changed
+329
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
## 目标
2+
完成错误提示信息的封装
3+
完成401错误跳转登录页的封装
4+
完成restful请求组合式api的封装
5+
## 错误提示信息
6+
当我们的接口请求发生错误的时候,我们不可能直接在控制台进行打印,对于用户的体验是非常差的,所以我们需要在页面中弹出对应的提示信息。
7+
首先我们需要让我们的提示信息支持多语言,在lang/global下面的zh-CN.ts和en-US.ts中分别导入:
8+
```typescript
9+
// 请求多语言配置
10+
'global.request.error.401': 'Login Expired',
11+
'global.request.error.403': 'Resource Request Error',
12+
'global.request.error.500': 'Server Error',
13+
'global.request.error.other': 'System Error',
14+
```
15+
```typescript
16+
// 请求多语言配置
17+
'global.request.error.401': '登录过期',
18+
'global.request.error.403': '资源请求错误',
19+
'global.request.error.500': '服务器错误',
20+
'global.request.error.other': '系统错误',
21+
```
22+
23+
我们可以使用naive-ui给我们提供的notification组件来实现,接下来我们先来写一下这一部分的代码。
24+
在request.ts中:
25+
```typescript
26+
const errorHandler = (error: AxiosError): Promise<any> => {
27+
const notification = useNotification()
28+
// 判断是否存在response
29+
if (error.response) {
30+
const { data, status, statusText } = error.response as AxiosResponse<any>
31+
if (status === 401) {
32+
// 重新登录
33+
notification.error({
34+
title: i18n.global.t('global.request.error.401'),
35+
content: data?.msg || statusText,
36+
duration: 3000,
37+
})
38+
}
39+
else if (status === 403) {
40+
//
41+
notification.error({
42+
title: i18n.global.t('global.request.error.403'),
43+
content: data?.msg || statusText,
44+
duration: 3000,
45+
})
46+
}
47+
else if (status === 500) {
48+
notification.error({
49+
title: i18n.global.t('global.request.error.500'),
50+
content: data?.msg || statusText,
51+
duration: 3000,
52+
})
53+
}
54+
else {
55+
notification.error({
56+
title: i18n.global.t('global.request.error.other'),
57+
content: data?.msg || statusText,
58+
duration: 3000,
59+
})
60+
}
61+
}
62+
return Promise.reject(error)
63+
}
64+
```
65+
我们测试一下会发现当我们点击401请求的时候,不仅没有弹出框,而且报错告诉我们inject只能在setup中使用或者函数式组件,所以我们在这里没办法直接使用的。
66+
![image.png](https://cdn.nlark.com/yuque/0/2022/png/10377041/1669022587644-338bfdbf-98f2-4dd8-ae39-29fab37b6331.png#averageHue=%23202225&clientId=u536c9bdd-ff67-4&from=paste&height=89&id=u6042e2ac&name=image.png&originHeight=89&originWidth=728&originalType=binary&ratio=1&rotation=0&showTitle=false&size=3993&status=done&style=none&taskId=u69f76a06-6fcc-4dc7-8293-73d8d1213ee&title=&width=728)
67+
那么我们来改进一下让他能使用,我们在compsables创建一个全局配置的组合式api,global-config.ts的文件,然后实现如下:
68+
```typescript
69+
import { merge } from 'lodash-es'
70+
71+
interface GlobalConfigType {
72+
notification?: ReturnType<typeof useNotification>
73+
message?: ReturnType<typeof useMessage>
74+
dialog?: ReturnType<typeof useDialog>
75+
loadingBar?: ReturnType<typeof useLoadingBar>
76+
}
77+
const globalConfig: GlobalConfigType = {
78+
}
79+
80+
export const useGlobalConfig = (): GlobalConfigType => {
81+
return globalConfig
82+
}
83+
84+
export const useGlobalConfigProvider = (config: GlobalConfigType) => {
85+
merge(globalConfig, config)
86+
}
87+
88+
```
89+
然后在components中的app-provider中增加一个naive-provider.vue组件
90+
```vue
91+
<script lang="ts" setup>
92+
const notification = useNotification()
93+
const dialog = useDialog()
94+
const message = useMessage()
95+
const loadingBar = useLoadingBar()
96+
useGlobalConfigProvider({
97+
notification,
98+
dialog,
99+
message,
100+
loadingBar,
101+
})
102+
</script>
103+
104+
<template>
105+
<slot />
106+
</template>
107+
108+
```
109+
然后在app-provider/index.vue中导入并使用
110+
```vue
111+
<template>
112+
<n-message-provider>
113+
<n-dialog-provider>
114+
<n-notification-provider>
115+
<n-loading-bar-provider>
116+
+ <naive-provider>
117+
<slot />
118+
+ </naive-provider>
119+
</n-loading-bar-provider>
120+
</n-notification-provider>
121+
</n-dialog-provider>
122+
</n-message-provider>
123+
</template>
124+
125+
```
126+
接下来我们调整request.ts中的引用:
127+
```typescript
128+
const errorHandler = (error: AxiosError): Promise<any> => {
129+
const { notification } = useGlobalConfig()
130+
// 判断是否存在response
131+
if (error.response) {
132+
const { data, status, statusText } = error.response as AxiosResponse<any>
133+
if (status === 401) {
134+
// 重新登录
135+
notification?.error({
136+
title: i18n.global.t('global.request.error.401'),
137+
content: data?.msg || statusText,
138+
duration: 3000,
139+
})
140+
}
141+
else if (status === 403) {
142+
//
143+
notification?.error({
144+
title: i18n.global.t('global.request.error.403'),
145+
content: data?.msg || statusText,
146+
duration: 3000,
147+
})
148+
}
149+
else if (status === 500) {
150+
notification?.error({
151+
title: i18n.global.t('global.request.error.500'),
152+
content: data?.msg || statusText,
153+
duration: 3000,
154+
})
155+
}
156+
else {
157+
notification?.error({
158+
title: i18n.global.t('global.request.error.other'),
159+
content: data?.msg || statusText,
160+
duration: 3000,
161+
})
162+
}
163+
}
164+
return Promise.reject(error)
165+
}
166+
167+
```
168+
然后测试,发现可以正常使用了。
169+
### 401跳转登录页面
170+
接下来我们完成一下401跳转登录页面的功能。
171+
首先我们项目中还没有登录页,我们先创建一个登录页面在pages中创建一个login文件夹,然后再创建一个index.vue的文件内容如下:
172+
```vue
173+
<script lang="ts" setup>
174+
175+
</script>
176+
177+
<template>
178+
<div>
179+
登录页面
180+
</div>
181+
</template>
182+
183+
```
184+
然后再配置一下路由
185+
```typescript
186+
import { createRouter, createWebHistory } from 'vue-router'
187+
import staticRoutes from '~/routes/static-routes'
188+
const router = createRouter({
189+
routes: [
190+
...staticRoutes,
191+
{
192+
path: '/login',
193+
component: () => import('~/pages/login/index.vue'),
194+
name: 'Login',
195+
},
196+
],
197+
history: createWebHistory(import.meta.env.VITE_APP_BASE ?? '/'),
198+
})
199+
200+
export default router
201+
202+
```
203+
最后我们在request.ts中401的部分增加跳转逻辑:
204+
```typescript
205+
if (status === 401) {
206+
// 重新登录
207+
notification?.error({
208+
title: i18n.global.t('global.request.error.401'),
209+
content: data?.msg || statusText,
210+
duration: 3000,
211+
})
212+
router.replace({ path: '/login' }).then(() => {
213+
/**
214+
* 这里处理清空用户信息和token的逻辑,后续扩展
215+
*/
216+
token.value = null
217+
})
218+
}
219+
```
220+
刷新页面测试跳转
221+
222+
### 封装restfulAPI
223+
我们整个项目的增删改查我们会使用restfulAPI的方式进行请求接下来我们一起封装一下增删改查的请求。
224+
在request.ts中:
225+
```typescript
226+
227+
export const useGet = <P = any, R = any>(url: string, params?: P, config?: AxiosRequestConfig): Promise< ResponseBody<R>> => {
228+
return instance.request({
229+
url,
230+
method: 'GET',
231+
params,
232+
...config,
233+
})
234+
}
235+
236+
export const usePost = <P = any, R = any>(url: string, data?: P, config?: AxiosRequestConfig): Promise< ResponseBody<R>> => {
237+
return instance.request({
238+
url,
239+
method: 'POST',
240+
data,
241+
...config,
242+
})
243+
}
244+
245+
export const usePut = <P = any, R = any>(url: string, data?: P, config?: AxiosRequestConfig): Promise< ResponseBody<R>> => {
246+
return instance.request({
247+
url,
248+
method: 'PUT',
249+
data,
250+
...config,
251+
})
252+
}
253+
254+
export const useDelete = <P = any, R = any>(url: string, data?: P, config?: AxiosRequestConfig): Promise< ResponseBody<R>> => {
255+
return instance.request({
256+
url,
257+
method: 'DELETE',
258+
data,
259+
...config,
260+
})
261+
}
262+
```
263+
然后我们在compsables中创建一个axios-fetch.ts的文件:
264+
```typescript
265+
import { useDelete, useGet, usePost, usePut } from '~/utils/request'
266+
267+
export {
268+
useGet,
269+
usePost,
270+
usePut,
271+
useDelete,
272+
}
273+
```
274+
实现自动导入的功能。
275+
276+
接下来我们进行测试,我们准备了四个请求如下:
277+
```shell
278+
# get
279+
https://mock.28yanyu.cn/mock/637af0d4080d2f1284a9e77b/test/200
280+
# post
281+
https://mock.28yanyu.cn/mock/637af0d4080d2f1284a9e77b/test/post
282+
# put
283+
https://mock.28yanyu.cn/mock/637af0d4080d2f1284a9e77b/test/put
284+
# delete
285+
https://mock.28yanyu.cn/mock/637af0d4080d2f1284a9e77b/test/delete
286+
```
287+
接下来我们在pages/index.vue中进行测试
288+
```vue
289+
<script lang="ts" setup>
290+
const onRequest1 = async () => {
291+
await useGet('https://mock.28yanyu.cn/mock/637af0d4080d2f1284a9e77b/test/200')
292+
}
293+
const onRequest2 = async () => {
294+
await usePost('https://mock.28yanyu.cn/mock/637af0d4080d2f1284a9e77b/test/post')
295+
}
296+
const onRequest3 = async () => {
297+
await usePut('https://mock.28yanyu.cn/mock/637af0d4080d2f1284a9e77b/test/put')
298+
}
299+
300+
const onRequest4 = async () => {
301+
await useDelete('https://mock.28yanyu.cn/mock/637af0d4080d2f1284a9e77b/test/delete')
302+
}
303+
</script>
304+
305+
<template>
306+
<div>
307+
<n-space>
308+
<n-button @click="onRequest1">
309+
get
310+
</n-button>
311+
<n-button @click="onRequest2">
312+
post
313+
</n-button>
314+
<n-button @click="onRequest3">
315+
put
316+
</n-button>
317+
<n-button @click="onRequest4">
318+
delete
319+
</n-button>
320+
</n-space>
321+
</div>
322+
</template>
323+
324+
<style scoped>
325+
326+
</style>
327+
328+
```
329+
测试是否能够请求成功

0 commit comments

Comments
 (0)