1.4.10 • Published 5 months ago
api-interceptor v1.4.10
api-interceptor
接口拦截器
Introduction
用于接口请求库实现签名、获取 token 以及更新 token 等功能,通过在拦截器(interceptor)中计算签名,并赋值到headers["Authorization-Sign"]
。适用于applets-request-mode-list
和axios
等实现拦截器的库。
- 特别注意:由于
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
,对应于headers
中
Authorization-AppKey`
appCode
Type: String
接口签名的appCode
或者secret
getTokenRequestInterceptor
参数
getToken
Type: Function
用于获取 token
refreshTokenResponseInterceptor
参数
isRefresh
Type: Boolean
是否重新获取 token
getToken
Type: Function