Vue 3 + TypeScript 封装 Axios 工具类

470 阅读2分钟

基础封装

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'

class AxiosService {
  private axiosInstance: AxiosInstance

  constructor() {
    this.axiosInstance = axios.create({
      baseURL: 'https://api.example.com',
      timeout: 5000,
    })

    this.initInterceptor()
  }

  private initInterceptor() {
    this.axiosInstance.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        // 在请求发送之前做些什么
        return config
      },
      (error: any) => {
        // 对请求错误做些什么
        return Promise.reject(error)
      }
    )

    this.axiosInstance.interceptors.response.use(
      (response: AxiosResponse<any>) => {
        // 对响应数据做些什么
        return response
      },
      (error: any) => {
        // 对响应错误做些什么
        return Promise.reject(error)
      }
    )
  }

  public get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
    return this.axiosInstance.get<T>(url, config)
  }

  public post<T = any>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.axiosInstance.post<T>(url, data, config)
  }

  public put<T = any>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.axiosInstance.put<T>(url, data, config)
  }

  public delete<T = any>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.axiosInstance.delete<T>(url, config)
  }
}

export const axiosService = new AxiosService()

这里,我们首先使用 axios.create 方法创建了一个基本的 Axios 实例,设置了 baseURLtimeout,然后初始化了请求和响应拦截器,以便在请求发送和响应到达时进行适当的处理。

接下来,我们在 AxiosService 类中定义了一些公共方法来处理 getpostputdelete 请求。这些方法都是通过基本的 Axios 实例来实现的。

最后,我们导出了一个 axiosService 实例,以便在 Vue 组件或其他地方使用。这样,我们就可以轻松地从代码中的任何地方发出 HTTP 请求,而无需在每个组件中都重复定义 Axios 实例。

我们可以在 initInterceptor() 方法中添加一个请求拦截器,用于在每个请求之前向请求头中添加 token。以下是修改后的代码:

...

  private initInterceptor() {
    // 添加请求拦截器
    this.axiosInstance.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        // 在请求发送之前做些什么
        const token = localStorage.getItem('token')
        if (token) {
          // 添加 token 到请求头
          config.headers.Authorization = `Bearer ${token}`
        }
        return config
      },
      (error: any) => {
        // 对请求错误做些什么
        return Promise.reject(error)
      }
    )

   ...

在这里,我们在 initInterceptor() 方法中添加了一个请求拦截器,用于在请求发送之前将 token 添加到请求头中。我们首先使用 localStorage 从本地存储中获取 token,然后将其添加到请求头中。

这样,在每个请求发送之前,都会调用这个请求拦截器,并在请求头中添加 token。这使得我们不必在每个请求中都手动添加 token,从而提高了代码的可重用性。

initInterceptor() 方法中添加一个响应拦截器,用于处理 HTTP 响应中的错误。以下是修改后的代码:

...
// 添加响应拦截器
    this.axiosInstance.interceptors.response.use(
      (response: AxiosResponse<any>) => {
        // 对响应数据做些什么
        return response
      },
      (error: any) => {
        // 对响应错误做些什么
        if (error.response) {
          // 如果服务器返回错误状态码
          switch (error.response.status) {
            case 401:
              // 未授权,跳转到登录页
              break
            case 404:
              // 请求的资源不存在
              break
            default:
              // 其他错误
              break
          }
        } else if (error.request) {
          // 如果请求发出去没有收到响应
          console.log(error.request)
        } else {
          // 其他错误
          console.log('Error', error.message)
        }
        return Promise.reject(error)
      }
    )
  }
...

在这里,我们添加了一个响应拦截器,用于处理 HTTP 响应中的错误。如果服务器返回错误状态码,我们可以在这里进行处理。例如,如果返回状态码为 401,表示未授权,我们可以将用户重定向到登录页面。如果返回状态码为 404,则表示请求的资源不存在。对于其他错误,我们可以根据需要进行处理。如果请求发出去没有收到响应,则 error.request 为请求的实例。如果出现其他错误,则 error.message 包含错误的描述。

在响应拦截器中可以判断一些常见的后端返回结果,例如:

  • success 字段为 false0,表示请求失败。
  • code 字段为某个错误码,例如 400、401、404 等。
  • message 字段包含错误信息,可以用来提示用户。

以下是修改后的完整代码:

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'

enum ResponseCode {
  Success = 0, // 请求成功
  Unauthorized = 401, // 未授权
  NotFound = 404, // 资源不存在
  InternalServerError = 500, // 服务器内部错误
}

interface ResponseData<T = any> {
  success: boolean;
  code: ResponseCode;
  message: string;
  data?: T;
}

class AxiosService {
  private axiosInstance: AxiosInstance

  constructor() {
    this.axiosInstance = axios.create({
      baseURL: 'https://api.example.com',
      timeout: 5000,
    })

    this.initInterceptor()
  }

  private initInterceptor() {
    // 添加请求拦截器
    this.axiosInstance.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        // 在请求发送之前做些什么
        const token = localStorage.getItem('token')
        if (token) {
          // 添加 token 到请求头
          config.headers.Authorization = `Bearer ${token}`
        }
        return config
      },
      (error: any) => {
        // 对请求错误做些什么
        return Promise.reject(error)
      }
    )

    // 添加响应拦截器
    this.axiosInstance.interceptors.response.use(
      (response: AxiosResponse<ResponseData>) => {
        // 对响应数据做些什么
        const { success, code, message, data } = response.data
        if (!success || code !== ResponseCode.Success) {
          // 请求失败或者出现错误
          console.error(`[${code}] ${message}`)
          return Promise.reject(new Error(message))
        }
        return data
      },
      (error: any) => {
        // 对响应错误做些什么
        if (error.response) {
          // 如果服务器返回错误状态码
          switch (error.response.status) {
            case ResponseCode.Unauthorized:
              // 未授权,跳转到登录页
              break
            case ResponseCode.NotFound:
              // 请求的资源不存在
              break
            default:
              // 其他错误
              break
          }
        } else if (error.request) {
          // 如果请求发出去没有收到响应
          console.log(error.request)
        } else {
          // 其他错误
          console.log('Error', error.message)
        }
        return Promise.reject(error)
      }
    )
  }

  public get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
    return this.axiosInstance.get<T>(url, config)
  }

  public post<T = any>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.axiosInstance.post<T>(url, data, config)
  }

  public put<T = any>(
    url: string,
    data?: any,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.axiosInstance.put<T>(url, data, config)
  }

  public delete<T = any>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.axiosInstance.delete<T>(url, config)
  }
}

export const axiosService = new AxiosService()

接下来就是使用这个工具类了

下面是一个使用封装好的 axios 工具类进行登录的例子:

// import axiosService from './axiosService'

interface LoginParams {
  username: string;
  password: string;
}

interface LoginResult {
  token: string;
}

export async function login(params: LoginParams): Promise<LoginResult> {
  try {
    const response = await axiosService.post<LoginResult>('/login', params)
    // 处理登录成功逻辑
    return response
  } catch (error) {
    // 处理登录失败逻辑
    return Promise.reject(error)
  }
}