Wrapper 模式
适用场景:给非 Axios 的请求方式(fetch、wx.request、自定义 SDK 等)接入全局守护,或者只想给单个请求函数加保护。
为什么需要 Wrapper?
Wrapper 就是为给其他非 Axios 的请求方式,提供同等的全局治理能力 —— 同样支持 rules、defaults、notify、logger。
用法一:非 Axios 请求全局接入
项目使用 fetch / wx.request / 自定义 SDK 等,希望像 Axios 模式一样拥有全局配置、规则匹配和消息通知。
第一步:将请求函数封装成 Promise 风格(如果原本就是 Promise 可跳过)
javascript
function wxRequest(config) {
return new Promise((resolve, reject) => {
wx.request({
...config,
success: (res) => resolve(res),
fail: (err) => reject(err)
});
});
}第二步:通过 setupRequestGuard 接入全局守护
javascript
import { setupRequestGuard, ConsoleRequestGuardLogger } from '@hydd/request-guard/core';
import { duplicate, retry } from '@hydd/request-guard/capabilities';
const guardedRequest = setupRequestGuard(wxRequest, {
capabilities: [
duplicate(),
retry()
],
notify: (payload) => wx.showToast({ title: payload.message, icon: 'none' }),
logger: new ConsoleRequestGuardLogger({ devOnly: true }),
// 全局规则 — 和 Axios 模式写法完全一致
rules: [
{ method: 'post', duplicate: { strategy: 'block', message: '正在提交中' } },
{ method: 'get', retry: { attempts: 2, delay: 500 } }
]
});
// 使用方式和原来一样,传入标准 config 对象即可
const res = await guardedRequest({
url: '/api/order/submit',
method: 'POST',
data: { orderId: '12345' },
header: { 'Content-Type': 'application/json' }
});
// 某个请求需要单独配置(请求级优先级最高)
await guardedRequest({
url: '/api/important',
method: 'GET',
requestGuard: { retry: { attempts: 5 } }
});
// 某个请求跳过守护
await guardedRequest({
url: '/api/health',
method: 'GET',
requestGuard: false // 总开关关闭
});
// 卸载守护,后续调用直接走原始函数
guardedRequest.uninstall();TIP
当请求函数的第一个参数就是包含 url/method/data 的标准 config 对象时,不需要额外配置 resolveArgs,guard 会自动识别。
用法二:只给单个请求加守护
项目中大部分请求不需要守护,只有某个特定请求函数需要保护(不区分 Axios 还是其他请求库):
javascript
import { setupRequestGuard } from '@hydd/request-guard';
import { submitOrder } from './api'; // 你的某个请求函数
// 只需要包裹这一个函数,最小化接入
const guardedSubmit = setupRequestGuard(submitOrder, {
notify: (payload) => Toast.show(payload.message),
rules: [{ method: 'post', duplicate: { strategy: 'block', message: '订单提交中' } }]
});
// 调用方式不变
const result = await guardedSubmit({ url: '/api/order', method: 'POST', data: formData });自定义参数适配(非标准 config 参数)
当你的请求函数第一个参数不是标准 config 对象,或者函数有多个入参时(比如是 url 字符串、或多参数签名),可以通过适配函数说明参数如何映射:
| 钩子 | 作用 | 必须 |
|---|---|---|
resolveArgs(args) | 从函数入参中解析出标准 config 对象 | 非标准签名时必须 |
mapConfig(config, args, context) | 将业务字段映射成 guard 标准字段(url/method/data/headers) | 字段名不一致时需要 |
getRequestGuard(config, args, context) | 从业务参数中提取 requestGuard 配置 | requestGuard 不在 config 顶层时需要 |
onBeforeRequest({ args, config, sourceConfig, context }) | 真实请求发出前触发,可修改最终参数 | 可选 |
onAfterResponse({ response, config, sourceConfig, context }) | 请求成功后触发,可做统一业务处理 | 可选 |
onAfterError({ error, config, sourceConfig, context }) | 请求失败后触发,可做统一错误上报 | 可选 |
场景一:函数签名为 request(url, config)
javascript
// 调用方式:guardedRequest('/api/submit', { method: 'POST', data: { id: 1 } })
const guardedRequest = setupRequestGuard(request, {
resolveArgs(args) {
const [url, config = {}] = args;
return { args, config: { url, ...config } };
}
});场景二:多参数 SDK 签名 httpClient(method, url, data, options)
javascript
// 调用方式:guardedClient('POST', '/api/submit', { id: 1 }, { timeout: 5000 })
const guardedClient = setupRequestGuard(httpClient, {
resolveArgs(args) {
const [method, url, data, options = {}] = args;
// configIndex: 3 — 把处理后的 config 回写到 args[3] 的位置
return { args, config: { method, url, data, ...options }, configIndex: 3 };
}
});场景三:完整配置(所有钩子联合使用)
假设项目中有一个自定义 SDK,字段名非标准、requestGuard 配置放在特殊位置、需要统一注入 token 和错误上报:
javascript
import { setupRequestGuard } from '@hydd/request-guard';
import { sdkRequest } from './sdk';
// SDK 调用方式:sdkRequest({ api, type, payload, extra })
const guardedSdk = setupRequestGuard(sdkRequest, {
notify: (payload) => Toast.show(payload.message),
// 1. resolveArgs:第一个参数就是 config,不需要合并,直接返回
resolveArgs(args) {
return { args, config: args[0] };
},
// 2. mapConfig:字段名不标准,映射成 guard 能识别的 url/method/data/headers
mapConfig(config) {
return {
url: config.api,
method: (config.type || 'GET').toUpperCase(),
data: config.payload,
headers: config.extra?.headers
};
},
// 3. getRequestGuard:requestGuard 配置在 config.extra.guard
getRequestGuard(config) {
return config.extra?.guard;
},
// 4. onBeforeRequest:请求发出前注入 token
onBeforeRequest({ args, config, sourceConfig }) {
const token = getToken();
if (token && sourceConfig.extra) {
sourceConfig.extra.headers = {
...sourceConfig.extra?.headers,
Authorization: `Bearer ${token}`
};
}
},
// 5. onAfterResponse:请求成功后统一处理业务错误码
onAfterResponse({ response, config }) {
if (response?.code !== 0) {
console.warn(`[业务异常] ${config.url}:`, response.message);
}
},
// 6. onAfterError:请求失败后上报错误
onAfterError({ error, config }) {
reportError({ url: config.url, message: error.message });
},
rules: [
{ method: 'post', duplicate: { strategy: 'block', message: '操作处理中' } }
]
});
// 使用方式和原 SDK 完全一致
const result = await guardedSdk({
api: '/user/bindPhone',
type: 'POST',
payload: { phone: '13800138000', code: '1234' },
extra: {
headers: { 'X-Source': 'app' },
guard: { duplicate: { message: '正在绑定中...' } } // 请求级守护配置
}
});