简单的封装一下axios

首先要弄清楚为什么需要封装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中捕获的错误就是在响应拦截器里面传递的错误:

查看一下浏览器控制台:

相关推荐
Nejosi_念旧几秒前
详细讲解css的穿透方法
前端·css·vue.js
shibin3 分钟前
从零到一:手把手搭建现代化 Webpack 5 项目
vue.js·react.js·webpack
辉夜真是太可爱啦11 分钟前
[组件封装]关于Vue3中如何封装多个v-model
前端·vue.js
宁静_致远1 小时前
Vue技术演进全解析:2.0到3.0的完整升级路线图
前端·vue.js
冷琴19961 小时前
基于python的租房网站-房屋出租租赁系统(python+django+vue)源码+运行步骤
vue.js·python·django
JianZhen✓2 小时前
前端使用vue,一个项目从零开始开发大概构思
前端·javascript·vue.js
阴谋与酒瓶2 小时前
深度脚本解析npm run dev做了什么
前端·vue.js·webpack
wkj0012 小时前
uniapp uni-drawer组件vue3写法
前端·vue.js·uni-app
慢知行3 小时前
前端权限管理实践:项目实战中的权限设计与实现
前端·vue.js·面试
计算机学姐3 小时前
基于SpringBoot的名著阅读网站
java·vue.js·spring boot·后端·mysql·spring·intellij-idea