TypeScript封装axios——基本封装与拦截器精细控制

805 阅读4分钟

1、基本封装

封装一个Request的类,使得在外部可以调用此类的构造函数创建实例,创建的实例就对应axios实例,request/index.ts中代码如下:

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";

class HDRequest {
  // 创建这个类的目的:每个创建出的HDRequest的实例都对应一个axios实例

  // 创建实例的方法:constructor()构造实例
  instance: AxiosInstance;
  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config);
  }

  // 二次封装网络请求的方法
  request(config: AxiosRequestConfig) {
    return this.instance.request(config);
  }
}

export default HDRequest;

基本配置信息单独写在一个文件中,config/index.ts中代码如下:

export const BASE_URL = "http://codercba.com:8000";
export const TIME_OUT = 5000;

在service/index.ts中创建一个Request类的一个实例,并配置这个实例:

import HDRequest from "./request";
import { BASE_URL, TIME_OUT } from "./config/index";

const hdRequest = new HDRequest({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
});

export default hdRequest;

在moduleds/home.ts中使用这个实例进行request:

import hdRequest from "..";

hdRequest
  .request({
    url: "/home/multidata",
  })
  .then((res) => {
    console.log(res.data);
  });

2、配置全局拦截器

request/index.ts

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";

class HDRequest {
  // 创建这个类的目的:每个创建出的HDRequest的实例都对应一个axios实例

  // 创建实例的方法:constructor()构造实例
  instance: AxiosInstance;
  constructor(config: HDRequestConfig) {
    this.instance = axios.create(config);

    // 添加全局拦截器,每个实例都有
    this.instance.interceptors.request.use(
      (config) => {
        console.log("全局请求成功的拦截");
        return config;
      },
      (err) => {
        console.log("全局请求失败的拦截");
        return err;
      }
    );
    this.instance.interceptors.response.use(
      (config) => {
        console.log("全局响应成功的拦截");
        return config;
      },
      (err) => {
        console.log("全局响应失败的拦截");
        return err;
      }
    );
  }

  // 二次封装网络请求的方法
  request(config: AxiosRequestConfig) {
    return this.instance.request(config);
  }
}

export default HDRequest;

3、为某一Request实例单独配置拦截器

新创建一个hdRequest2实例,在它的config中传入拦截器属性,但是axios的AxiosRequestConfig类型中并没有拦截器属性类型。

因此需要对request/index.ts中的构造函数中的config类型进行扩展(extends)

import HDRequest from "./request";
import { BASE_URL, TIME_OUT } from "./config/index";

const hdRequest = new HDRequest({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
});

// 创建一个能够自定义拦截器的request实例
export const hdRequest2 = new HDRequest({
  baseURL: "http://codercba.com:1888/airbnb/api",
  timeout: 8000,
  
  // 传入拦截器属性,但是axios的AxiosRequestConfig类型中并没有拦截器属性类型
  // 因此需要对request/index.ts中的构造函数中的config类型进行扩展(extends)
  interceptors: {
    requestSuccessFn: (config) => {
      console.log("爱彼迎的请求成功的拦截");
      return config;
    },
    requestFailureFn: (err) => {
      console.log("爱彼迎的请求失败的拦截");
      return err;
    },
    responseSuccessFn: (res) => {
      console.log("爱彼迎的响应成功的拦截");
      return res;
    },
    responseFailureFn: (err) => {
      console.log("爱彼迎的响应失败的拦截");
      return err;
    },
  },
});

export default hdRequest;

request/index.ts

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";

interface HDInteceptors {
  requestSuccessFn?: (config: AxiosRequestConfig) => AxiosRequestConfig;
  requestFailureFn?: (err: any) => any;
  responseSuccessFn?: (config: AxiosResponse) => AxiosResponse;
  responseFailureFn?: (err: any) => any;
}
interface HDRequestConfig extends AxiosRequestConfig {
  interceptors?: HDInteceptors;
}

class HDRequest {
  // 创建这个类的目的:每个创建出的HDRequest的实例都对应一个axios实例

  // 创建实例的方法:constructor()构造实例
  instance: AxiosInstance;
  constructor(config: HDRequestConfig) {
    this.instance = axios.create(config);

    // 添加全局拦截器,每个实例都有
    this.instance.interceptors.request.use(
      (config) => {
        console.log("全局请求成功的拦截");
        return config;
      },
      (err) => {
        console.log("全局请求失败的拦截");
        return err;
      }
    );
    this.instance.interceptors.response.use(
      (config) => {
        console.log("全局响应成功的拦截");
        return config;
      },
      (err) => {
        console.log("全局响应失败的拦截");
        return err;
      }
    );

    // 针对特定的hdRequest实例添加拦截器
    this.instance.interceptors.request.use(
      // 使用可选链,若config中没传入interceptors,那么这里是undefined
      config.interceptors?.requestSuccessFn,
      config.interceptors?.requestFailureFn
    );

    this.instance.interceptors.response.use(
      config.interceptors?.responseSuccessFn,
      config.interceptors?.requestFailureFn
    );
  }

  // 二次封装网络请求的方法
  request(config: AxiosRequestConfig) {
    return this.instance.request(config);
  }
}

export default HDRequest;

4、同一个request实例的不同网络请求设置不同的拦截器

modules/entire.ts中同一个实例在发送不同的request请求时一个配置了拦截器一个没配拦截器

import { hdRequest2 } from "..";

hdRequest2
  .request({
    url: "/entire/list",
    params: {
      offset: 0,
      size: 20,
    },
  })
  .then((res) => {
    console.log(res);
  });

hdRequest2
  .request({
    url: "/home/highscore",
    interceptors: {
      requestSuccessFn: (config) => {
        console.log("/home/highscore请求成功的拦截");
        return config;
      },
      responseSuccessFn: (res) => {
        console.log("/home/highscore响应成功的拦截");
        return res;
      },
    },
  })
  .then((res) => {
    console.log(res);
  });

对request/index.ts的request方法进行进一步封装,使之能够立即执行传进来的拦截器

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";

interface HDInteceptors {
  requestSuccessFn?: (config: AxiosRequestConfig) => AxiosRequestConfig;
  requestFailureFn?: (err: any) => any;
  responseSuccessFn?: (res: AxiosResponse) => AxiosResponse;
  responseFailureFn?: (err: any) => any;
}
interface HDRequestConfig extends AxiosRequestConfig {
  interceptors?: HDInteceptors;
}

class HDRequest {
  // 创建这个类的目的:每个创建出的HDRequest的实例都对应一个axios实例

  // 创建实例的方法:constructor()构造实例
  instance: AxiosInstance;
  constructor(config: HDRequestConfig) {
    this.instance = axios.create(config);

    // 添加全局拦截器,每个实例都有
    this.instance.interceptors.request.use(
      (config) => {
        console.log("全局请求成功的拦截");
        return config;
      },
      (err) => {
        console.log("全局请求失败的拦截");
        return err;
      }
    );
    this.instance.interceptors.response.use(
      (config) => {
        console.log("全局响应成功的拦截");
        return config;
      },
      (err) => {
        console.log("全局响应失败的拦截");
        return err;
      }
    );

    // 针对特定的hdRequest实例添加拦截器
    this.instance.interceptors.request.use(
      config.interceptors?.requestSuccessFn,
      config.interceptors?.requestFailureFn
    );

    this.instance.interceptors.response.use(
      config.interceptors?.responseSuccessFn,
      config.interceptors?.requestFailureFn
    );
  }

  // 二次封装网络请求的方法
  request(config: HDRequestConfig) {
    // 为同一个request实例的不同网络请求设置不同的拦截器
    // 不能将拦截器放在实例上,这样的话同一个实例的拦截器都是一样的了
    // 只能判断传进来的config中是否设置了拦截器,若设置了就直接执行
      
    // 执行this.instance.request(config)之前先执行requeSuccess,并更新config
    if (config.interceptors?.requestSuccessFn) {
      //立即调用拦截器函数执行requestSuccessFn
      config = config.interceptors.requestSuccessFn(config);
    }

    // 由于执行完this.instance.request(config)之后才能对response结果进行拦截,是个异步的过程
    // 创建一个Promise,先执行this.instance.request(config),然后等待结果,之后以结果作为拦截器函数的参数进行调用
    return new Promise((resolve, reject) => {
      this.instance
        .request(config)
        .then((res) => {
          if (config.interceptors?.responseSuccessFn) {
            res = config.interceptors.responseSuccessFn(res);
          }
          resolve(res);
        })
        .catch((err) => {
          reject(err);
        });
    });
  }
}

export default HDRequest;