首先要弄清楚为什么需要封装axios,不能盲目的为了封装而封装。
封装axios是为了统一处理:
- api根路径
- 超时时间
- Content-Type(发送给服务器的数据类型,服务器会根据该信息来解析请求体内容。)
- 请求拦截(每次给服务器发送请求之前对请求进行统一处理,比如header中携带token等)
- 响应拦截(统一处理服务器返回的数据,比如错误响应怎么处理)
安装axios
text
# npm 安装
npm install axios
# pnpm 安装
pnpm install axios
跟后端约定返回数据结构
比如,我们的约定是这样的:
json
{
"code": 0,
"msg": "",
"data": null
}
code:
0
代表失败
1
代表成功
msg:
描述信息
data:
后端返回的有效数据都在data里面
java代码:
java
package com.akbar.pojo.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 统一返回工具类
* @param <T>
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private Integer code; // 1成功,0和其它数字为失败
private String msg; // //描述信息
private T data; // //数据
public static final Integer SUCCESS_CODE = 1;
public static final Integer ERROR_CODE = 0;
public static <T> Result<T> success(T data) {
return new Result<>(SUCCESS_CODE, "Success", data);
}
public static <T> Result<T> success() {
return new Result<>(SUCCESS_CODE, "Success", null);
}
public static <T> Result<T> error(String message) {
return new Result<>(ERROR_CODE, message, null);
}
}
开始封装axios
ts
import axios from 'axios'
import { getToken, removeToken } from './localstorage'
// 1.根域名,超时时间配置
const request = axios.create({
baseURL: '/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
})
// 2.请求拦截器
request.interceptors.request.use(
(config) => {
// 每次请求都携带token
const token = getToken()
if (token) {
// 这里后端要求是用Authorization作为token-name,具体看后端怎么要求
config.headers.Authorization = token
}
return config
},
(err) => {
return Promise.reject(err)
}
)
// 3.响应拦截器
request.interceptors.response.use(
(response) => {
const { code, msg, data } = response.data
if (code === 1) {
return data // 直接返回data
} else {
// 优先传递msg信息,如果没有msg信息,就传递:请求失败
// api调用处需要捕获这里返回的错误
return Promise.reject(new Error(msg || '请求失败'))
}
},
(error) => {
// 监控401状态码
if (error.response?.status === 401) {
removeToken()
// 跳转到login页面
// 直接跳转到login页面就好了,不用返回什么错误信息
// 我试过返回错误信息,但是有时候很别扭
}
// 为了避免err.response是undefined
// 报这样的错误Uncaught TypeError: Cannot read properties of undefined (reading 'data')
// 所以这里写问号(?)
return Promise.reject(new Error(error.response?.data?.msg || '服务器端错误'))
}
)
// 按需到处还是默认到处都是你说了算
// 不要纠结到底用那种导出
export { request }
如果希望捕捉更详细的错误信息,可以把响应拦截器改成这样:
ts
// 3.响应拦截器
request.interceptors.response.use(
(response) => {
const { code, msg, data } = response.data
if (code === 1) {
return data // 直接返回data
} else {
return Promise.reject(new Error(msg || '请求失败'))
}
},
(error) => {
// 有响应情况下的错误
// 也就是说这些错误在前端跟后端连接正常的情况下发生
if (error.response) {
const { status, data } = error.response
switch (status) {
case 401:
// 处理 401 错误
removeToken()
// 跳转到login页面
return Promise.reject(new Error('未授权,请登录'))
case 500:
// 处理 500 错误
return Promise.reject(new Error('服务器出现问题,请稍后再试'))
case 404:
// 处理 404 错误
return Promise.reject(new Error('请求的资源未找到'))
default:
// 处理其他错误
return Promise.reject(new Error(data?.msg || '服务器端错误'))
}
} else {
// 网络错误(没有响应)
// 也就是说没能跟后端连接成功
return Promise.reject(new Error('网络错误,无法连接到服务器'))
}
}
)
调用api
比如获取文章分类信息的接口为例:
ts
import { request } from '../utils/request'
export interface Category {
id: number
name: string
adminId: number
createdTime: string
updatedTime: string
}
export const fetchCategoryListAPI = (): Promise<Category[]> => {
return request({
url: '/category/list',
method: 'GET',
})
}
因为响应拦截器在成功状态下(code===1)直接把data给返回了,所以我们在Promise<Category[]>
这里只需要处理后端返回的data的结构就可以了
这就是后端返回的data
在vue组件里面调用这个api:
html
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { fetchCategoryListAPI, type Category } from './apis/category.ts'
const categories = ref<Category[]>([]) // 存储分类列表
onMounted(() => {
fetchCategoryListAPI()
.then((response) => {
categories.value = response
})
.catch((error) => {
console.error('获取分类失败:', error)
})
})
</script>
<template>
<ul>
<li v-for="category in categories" :key="category.id">
{{ category.name }}
</li>
</ul>
</template>
上面代码中:
ts
fetchCategoryListAPI()
.then((response) => {
categories.value = response
})
.catch((error) => {
console.error('获取分类失败:', error)
})
是比较老的写法,现在推荐使用async,await:
html
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { fetchCategoryListAPI, type Category } from './apis/category.ts'
const categories = ref<Category[]>([]) // 存储分类列表
// async,await写法
const getCategories = async () => {
try {
const response = await fetchCategoryListAPI()
categories.value = response
} catch (error) {
if(error instanceof Error) {
console.error('获取分类失败:', error.message)
}
}
}
onMounted(()=> {
getCategories()
})
</script>
<template>
<ul>
<li v-for="category in categories" :key="category.id">
{{ category.name }}
</li>
</ul>
</template>
在catch中捕获的错误就是在响应拦截器里面传递的错误:
查看一下浏览器控制台: