1.4.10 • Published 5 months ago

api-interceptor v1.4.10

Weekly downloads
-
License
MIT
Repository
-
Last release
5 months ago

api-interceptor

接口拦截器

Introduction

用于接口请求库实现签名、获取 token 以及更新 token 等功能,通过在拦截器(interceptor)中计算签名,并赋值到headers["Authorization-Sign"]。适用于applets-request-mode-listaxios等实现拦截器的库。

  • 特别注意:由于request interceptor的执行逻辑是先添加的后执行,所以如果签名中需要加入Authorization-Token一起参与签名,则sign-interceptor拦截器需要在获取 token 前加入拦截器

注意事项

拦截器执行顺序

  • 使用addRequestInterceptor添加请求拦截器时,先添加的拦截器函数,后执行,是一个栈的结构,先进后出。需要特别注意顺序
  • 使用addResponseInterceptor添加响应拦截器时,先添加的拦截器函数,先执行,是一个队列的结构,先进先出。

Install

npm install git+ssh://git@git.verystar.cn:common/api-interceptor.git#Tag

Install Example

npm install git+ssh://git@git.verystar.cn:common/api-interceptor.git#v1.3.1

API

signInterceptor(appKey, appCode)

返回一个函数,用于执行签名计算

getTokenRequestInterceptor(getToken)

返回一个函数,用于执行获取 token

refreshTokenResponseInterceptor(isRefresh, getToken)

返回一个函数,用于执行重新获取 token,并用新的 token 自动重新发起一次请求

generateErrorLog(err)

返回Json数据,用于生成接口请求的错误日志信息

generateRequestAfterLog(res)

返回Json数据,用于生成接口响应的日志信息

generateRequestBeforeLog(config)

返回Json数据,用于生成接口请求前的日志信息

Demo

import ApiHttp from "applets-request-mode-list";
import {
  signInterceptor,
  getTokenRequestInterceptor,
  refreshTokenResponseInterceptor,
  responseFormatterInterceptor,
  generateErrorLog,
  generateRequestAfterLog,
  generateRequestBeforeLog,
} from "api-interceptor";
import { getToken } from "mini-auth-v1";

// 接口配置
const apiList = {
  getDemo: {
    apiUrl: "/get/demo",
    method: "POST",
    /**
     * 重试次数
     *
     * 可选配置:默认2
     */
    retryTimes: 3,

    /**
     * 重试间隔时间
     *
     * 可选配置:默认2000
     */
    interval: 3000,
  },
};

function createApiHttp(
  options,
  reqConfig = {},
) {
  // 构建接口请求对象
  const api = new ApiHttp(options, reqConfig);

  // 设置请求超时时间
  api.appletsRequest.defaults.timeout = 10000;

  // 添加request拦截器,计算签名,先于获取token的拦截器
  api.addRequestInterceptor((config) => {
    if (config.apiConfig.skipSign === false) {
      return config;
    }
    const interceptor = signInterceptor("API_APP_KEY", "API_APP_CODE");
    return interceptor(config);
  });

  // 添加获取token拦截器
  api.addRequestInterceptor(async (config) => {
    if (config.apiConfig.requireToken === false) {
      return config;
    }
    // 传入getToken方法
    const interceptor = getTokenRequestInterceptor(getToken);
    // 执行 interceptor(config) 后,会在 config.tokenInterceptData 中增加 getToken 获取到的原始数据
    // 按需使用tokenInterceptData中的数据
    const intercept = await interceptor(config);
    return intercept;
  });

  // 添加请求前日志
  api.addRequestInterceptor((config) => {
    const logInfo = generateRequestBeforeLog(config);
    // logInfo = {
    //   v3: "",
    //   msg: "",
    //   request: string or object
    // }
    // 生成的logInfo可用于投递到日志系统中
    return config;
  });

  /**
   * 记录请求返回日志
   */
  api.addResponseInterceptor((res) => {
    const logInfo = generateRequestAfterLog(res);
    // logInfo = {
    //   v3: "",
    //   msg: "",
    //   request: string or object,
    //   response: string or object,
    // }
    // 可以使用logInfo,投递到日志系统
    return res;
  });

  api.addResponseInterceptor((res) => {
    // refreshTokenResponseInterceptor需要两个参数:isRefresh 和 getToken
    // 第一个参数判断是否是要重新请求,类型boolean,true表示重新请求token,执行getToken逻辑
    // 第二个参数获取token的方法:getToken
    const interceptor = refreshTokenResponseInterceptor(
      res.data.code === 40003,
      getToken,
    );
    return interceptor(res);
  });

  // 格式化输出请求返回数据
  api.addResponseInterceptor((responseFormatterInterceptor);

  /**
   * 记录请求返回错误日志
   */
  api.addResponseInterceptor((res) => res, (err) => {
    const logInfo = generateErrorLog(err);
    // logInfo = {
    //   v3: "",
    //   msg: "",
    //   request: string or object,
    //   response: string or object,
    // }
    // 可以使用logInfo,投递到日志系统
    return Promise.reject(err);
  });

  /**
   * 格式化错误信息
   */
  api.addResponseInterceptor((res) => res, responseErrorFormatter);

  return api;
}

const apiHttpOptions = {
  baseURL: "https://xxx.com",
  apiList,
};

// 设置所有的Content-Type为"application/json; charset=utf-8"
const requestConfig = {
  headers: {
    "Content-Type": "application/json; charset=utf-8",
  },
};

const apiObj = createApiHttp(apiHttpOptions, requestConfig);

export default apiObj.apis;

export { apiObj as apiHttp };

export { createApiHttp as createApiHttp };

// 接口请求示例
apiObj.apis.getDemo().then().catch();

TypeScript 版 Demo

/* eslint-disable space-before-function-paren */
import ApiHttp from "applets-request-mode-list";
import {
  signInterceptor,
  getTokenRequestInterceptor,
  refreshTokenResponseInterceptor,
  responseFormatterInterceptor,
  responseErrorFormatterInterceptor,
  generateErrorLog,
  generateRequestBeforeLog,
  generateRequestAfterLog,
} from "api-interceptor";
import { getToken } from "mini-auth-v1";

// 接口配置
const apiList = {
  getDemo: {
    apiUrl: "/get/demo",
    method: "POST",
    /**
     * 重试次数
     *
     * 可选配置:默认2
     */
    retryTimes: 3,

    /**
     * 重试间隔时间
     *
     * 可选配置:默认2000
     */
    interval: 3000,
  },
};

interface IAppletsResSuccess<IData = any> {
  retcode: number;
  msg: string;
  data: IData;
}

type IMainApis<IApiFn> = {
  [K in keyof IApiFn]: IApiFn[K] extends {
    params: Record<string, any>;
  }
    ? <IParam>(
        opts: IAppletsRequestConfig<IParam>,
      ) => Promise<IAppletsResSuccess<any>>
    : (opts: IAppletsRequestConfig | void) => Promise<IAppletsResSuccess<any>>;
};

interface ICreateApiHttpOptions {
  baseURL: string;
  apiList: IAppletsApi.IApiList;
}

/**
 * 创建请求对象实例
 * @param options ICreateApiHttpOptions
 * @param reqConfig IAppletsRequestConfig
 */
function createApiHttp<T = null>(
  options: ICreateApiHttpOptions,
  reqConfig?: IAppletsRequestConfig,
): ApiHttp<T> {
  const api = new ApiHttp<T>(options, reqConfig);

  api.appletsRequest.defaults.timeout = 10000;

  // 添加签名拦截器
  api.addRequestInterceptor((config) => {
    if (config.apiConfig.skipSign === false) {
      return config;
    }
    const interceptor = signInterceptor("API_APP_KEY", "API_APP_CODE");
    return interceptor(config);
  });

  // 添加获取token拦截器
  api.addRequestInterceptor(async (config) => {
    if (config.apiConfig.requireToken === false) {
      return config;
    }
    const interceptor = getTokenRequestInterceptor(getToken);
    // 执行 interceptor(config) 后,会在 config.tokenInterceptData 中增加 getToken 获取到的原始数据
    // 按需使用tokenInterceptData中的数据
    const intercept = await interceptor(config);
    return intercept;
  });

  // 添加请求前日志
  api.addRequestInterceptor((config) => {
    const logInfo = generateRequestBeforeLog(config);
    // logInfo = {
    //   v3: "",
    //   msg: "",
    //   request: string or object
    // }
    // 生成的logInfo可用于投递到日志系统中
    return config;
  });

  /**
   * 记录请求返回日志
   */
  api.addResponseInterceptor((res) => {
    const logInfo = generateRequestAfterLog(res);
    // logInfo = {
    //   v3: "",
    //   msg: "",
    //   request: string or object,
    //   response: string or object,
    // }
    // 可以使用logInfo,投递到日志系统
    return res;
  });

  // 添加更新token拦截器
  api.addResponseInterceptor((res) => {
    const interceptor = refreshTokenResponseInterceptor(
      // 需要更新token的错误码
      [40003, 40001].includes(res.data.code),
      getToken,
    );
    return interceptor(res);
  });

  // 格式化输出请求返回数据
  api.addResponseInterceptor(responseFormatterInterceptor);

  /**
   * 记录请求返回错误日志
   */
  api.addResponseInterceptor((res) => res, (err) => {
    const logInfo = generateErrorLog(err);
    // logInfo = {
    //   v3: "",
    //   msg: "",
    //   request: string or object,
    //   response: string or object,
    // }
    // 可以使用logInfo,投递到日志系统
    return Promise.reject(err);
  });

  /**
   * 格式化错误信息
   */
  api.addResponseInterceptor((res) => res, responseErrorFormatterInterceptor);

  return api;
}

const apiHttpOptions = {
  baseURL: "HOST",
  apiList: apiList as IAppletsApi.IApiList,
};

// 设置所有的Content-Type为"application/json; charset=utf-8"
const requestConfig = {
  headers: {
    "Content-Type": "application/json; charset=utf-8",
  },
};

const apiObj = createApiHttp<IMainApis<typeof apiList>>(apiHttpOptions, requestConfig);

export default apiObj.apis;

export { apiObj as apiHttp };

export { createApiHttp as createApiHttp };

// 接口请求示例
// options和axios中的参数一致
apiObj.apis.getDemo(options).then().catch();

signInterceptor参数

appKey

Type: String

接口签名的appKey,对应于headersAuthorization-AppKey`

appCode

Type: String

接口签名的appCode或者secret

getTokenRequestInterceptor参数

getToken

Type: Function

用于获取 token

refreshTokenResponseInterceptor参数

isRefresh

Type: Boolean

是否重新获取 token

getToken

Type: Function

依赖

点击查看applets-request-mode-list

1.4.10

5 months ago

1.4.9

5 months ago

1.4.8

5 months ago