import { Ctx } from './interfaces'; import { ERROR_STRINGS } from './constants'; import { runFn, combineUrls, checkArrayParams, getDefaultParamObj, } from './utils'; export function isServer(): boolean { return typeof window === 'undefined'; } export function isBrowser(): boolean { return !isServer(); } export function decodeUntilEqual(str = ''): string { let tmpStr = str; let decodedStr = ''; do { decodedStr = tmpStr; tmpStr = decodeURIComponent(decodedStr); } while (tmpStr !== decodedStr); return decodedStr; } export function getUrlParams(search = ''): Record { /* istanbul ignore next */ const searchString = search || (isBrowser() ? location.search : ''); const queryString = searchString.split('?')[1]; if (!queryString) return {}; return queryString .split('&') .reduce((acc, cur): { [k: string]: string; } => { const [key, val] = cur.split('='); return { ...acc, [key]: decodeUntilEqual(val), }; }, {}); } /** * 记录请求开始时间 * @param {object} ctx 上下文对象 * @param {Function} next 转移控制权给下一个中间件的函数 */ const recordStartTimeMiddleware = (ctx: Ctx, next: () => Promise) => { ctx.startTime = Date.now(); return next(); }; /** * 记录接受响应时间和请求总时间的中间件 * @param {object} ctx 上下文对象 * @param {Function} next 转移控制权给下一个中间件的函数 */ const recordReqTimeMiddleware = (ctx: Ctx, next: () => Promise) => next() .then(() => { ctx.endTime = Date.now(); ctx.reqTime = Date.now() - ctx.startTime; }); /** * 由于后台返回数据结构不统一,增加对于返回数组情况的兼容处理 * 且对于 code 进行强制类型转换 * @param {object} ctx 上下文对象(ctx.res.data 接口返回数据) * @param {Function} next 转移控制权给下一个中间件的函数 */ const formatResDataMiddleware = (ctx: Ctx, next: () => Promise) => next() .then(() => { const jsonData = ctx.res.data; ctx.res.rawData = ctx.res.data; if (!jsonData) return Promise.reject(Error(ERROR_STRINGS.noData)); if (Array.isArray(jsonData)) { const [code, data, msg] = jsonData; ctx.res.data = { code: +code, data, msg }; } else { ctx.res.data = { ...jsonData, code: +jsonData.code }; } }); /** * 生成请求函数所需参数 * @param {object} ctx 上下文对象 * @param {Function} next 转移控制权给下一个中间件的函数 */ const formatReqParamsMiddleware = (ctx: Ctx, next: () => Promise) => { const { args, params, commonParams: rawCommonParams, } = ctx.req; if (typeof args !== 'object') { throw TypeError(ERROR_STRINGS.paramsType); } checkArrayParams(ctx.req); const commonParams = runFn(rawCommonParams, args); // 根据配置生成请求的参数 if (Array.isArray(params)) { ctx.req.reqParams = { ...commonParams, ...args }; return next(); } ctx.req.reqParams = { ...getDefaultParamObj({ ...ctx.req, commonParams }), ...args, }; return next(); }; /** * 设置请求的 reqFnParams 参数,将被用于 _reqFn 函数 * @param {object} ctx 上下文对象 * @param {Function} next 转移控制权给下一个中间件的函数 */ const setReqFnParamsMiddleware = (ctx: Ctx, next: () => Promise) => { const { path, prefix, reqParams, baseUrl, ...rest } = ctx.req; // 请求地址 const url = combineUrls( combineUrls(baseUrl, prefix), runFn(path, reqParams), ); ctx.req.reqFnParams = { url, reqParams, ...rest, // 若是用户自己传递 reqFnParams 则优先级最高 ...ctx.req.reqFnParams, }; return next(); }; /** * 若 url query 上有 sportsenv,则将其传入 header * 背景,后台测试环境根据header中的 sports-env 字段决定 * 此插件从url中获取 sportsenv 参数,设置到请求的header中 * @since v2.1.0+ * @param ctx * @param next * @return {Function} 执行下一个middleware */ const setEnvFromUrlMiddleware = (ctx: Ctx, next: () => Promise) => { // 从env参数中获取值 const { sportsenv } = getUrlParams(); if (typeof sportsenv !== 'undefined' && sportsenv !== '') { ctx.req.reqFnParams.header = { ...ctx.req.reqFnParams.header, 'sports-env': sportsenv, }; // 若为 post 请求则默认添加 json 请求头 const isPost = ctx.req.method.toLowerCase() === 'post'; if (isPost) { ctx.req.reqFnParams.header['Content-Type'] = ctx.req.reqFnParams.header['Content-Type'] ?? 'application/json;charset=utf-8'; } } return next(); }; /** * 若 url query 上有 sportsenv,则请求接口换为传入域名 * 并设置 sports-env 到 header 中 * @param testBaseUrl 测试 baseUrl * @returns setEnvFromUrlMiddleware */ const getEnvFromUrlMiddlewareByTestBaseUrl = (testBaseUrl: string) => (ctx: Ctx, next: () => Promise) => { if (getUrlParams().sportsenv) { ctx.req.baseUrl = testBaseUrl; } return setEnvFromUrlMiddleware(ctx, next); }; export { recordReqTimeMiddleware, formatResDataMiddleware, setEnvFromUrlMiddleware, setReqFnParamsMiddleware, recordStartTimeMiddleware, formatReqParamsMiddleware, getEnvFromUrlMiddlewareByTestBaseUrl, };