避开这些坑!微信小程序请求拦截的3种实现方案对比(含自定义封装/中间件/代理模式)
微信小程序请求拦截实战三种方案的深度抉择指南在微信小程序开发中请求拦截是每个开发者迟早要面对的技术难题。想象一下这样的场景你的小程序需要对接多个后端服务有的要求数据加密传输有的需要自动添加认证令牌还有的可能要记录详细的请求日志。如果每个请求都手动处理这些逻辑代码很快就会变得臃肿不堪。这就是请求拦截技术大显身手的时候了——它能让你的代码保持优雅同时满足各种复杂的业务需求。1. 全局拦截方案Object.defineProperty的威力与陷阱当你需要为整个小程序的所有请求添加统一处理逻辑时Object.defineProperty无疑是最彻底的解决方案。这种方案通过重定义微信原生的wx.request方法实现了对请求的完全控制。1.1 核心实现原理全局拦截的核心在于巧妙地劫持微信的请求方法。以下是典型实现步骤const originalWx wx; // 保存原始wx对象 wx Object.create(wx); // 创建新对象继承wx Object.defineProperty(wx, request, { writable: false, value: function(config) { // 请求前处理 if (!isUrlInWhiteList(config.url)) { config encryptRequestData(config); } // 保存原始回调 const originalSuccess config.success; const originalFail config.fail; // 重写成功回调 config.success function(response) { if (response.statusCode 200) { response.data decryptResponseData(response.data); } originalSuccess originalSuccess(response); }; // 调用原始请求方法 originalWx.request.call(this, config); } });这段代码做了几件关键事情创建了原始wx对象的副本避免污染全局定义不可写的request属性防止被意外修改在请求发出前进行数据加密处理在响应返回后进行数据解密处理1.2 实战中的坑与解决方案在实际项目中我们遇到过几个典型问题问题1第三方库的请求被意外拦截有些UI库内部也使用wx.request可能不需要加密处理。解决方案是建立白名单机制function isUrlInWhiteList(url) { const whiteList [ /api/public, https://cdn.example.com ]; return whiteList.some(pattern url.includes(pattern)); }问题2异步加密导致的时序问题如果加密过程是异步的比如需要先获取加密密钥直接修改config会出问题。这时可以用Promise包装async function asyncEncrypt(config) { const key await getEncryptionKey(); return { ...config, data: encrypt(config.data, key) }; } // 在拦截器中 const encryptedConfig await asyncEncrypt(config); originalWx.request.call(this, encryptedConfig);1.3 适用场景评估这种方案最适合以下情况全站统一加密需求如金融类小程序强制性的全局逻辑如自动刷新token需要监控所有请求的场景如全链路日志但要注意它的调试成本较高。我们建议在正式使用前先实现一个日志开关const debug true; if (debug) { console.log(拦截请求:, config); config.success function(res) { console.log(拦截响应:, res); originalSuccess originalSuccess(res); }; }2. 模块化方案自定义请求类的灵活之道当你的项目采用模块化架构或者需要针对不同业务模块实现不同的拦截逻辑时自定义请求封装是更合适的选择。2.1 基础封装模式一个健壮的自定义请求类应该包含这些要素class HttpService { constructor(baseURL) { this.baseURL baseURL; this.interceptors { request: [], response: [] }; } request(config) { // 处理请求拦截器 let promise Promise.resolve(config); this.interceptors.request.forEach(interceptor { promise promise.then(interceptor); }); return promise.then(processedConfig { return new Promise((resolve, reject) { wx.request({ url: this.baseURL processedConfig.url, ...processedConfig, success: (res) { // 处理响应拦截器 let responsePromise Promise.resolve(res); this.interceptors.response.forEach(interceptor { responsePromise responsePromise.then(interceptor); }); responsePromise.then(resolve).catch(reject); }, fail: reject }); }); }); } get(url, params) { return this.request({ url, method: GET, data: params }); } post(url, data) { return this.request({ url, method: POST, data }); } }2.2 多模块差异化处理实战假设我们有两个业务模块用户模块需要AES加密订单模块需要RSA加密。可以这样实现// 用户服务 const userHttp new HttpService(https://api.example.com/user); userHttp.interceptors.request.push(config { return { ...config, data: aesEncrypt(config.data, USER_KEY) }; }); // 订单服务 const orderHttp new HttpService(https://api.example.com/order); orderHttp.interceptors.request.push(config { return { ...config, data: rsaEncrypt(config.data, ORDER_PUB_KEY) }; }); // 使用示例 userHttp.get(/profile, { userId: 123 }); orderHttp.post(/create, { productId: 456 });2.3 性能优化技巧自定义封装虽然灵活但频繁创建新实例会影响性能。我们推荐两种优化方案单例模式共享实例const httpInstances {}; function getHttpInstance(baseURL) { if (!httpInstances[baseURL]) { httpInstances[baseURL] new HttpService(baseURL); } return httpInstances[baseURL]; }拦截器共享策略const commonInterceptors { request: [addTimestamp, addSignature], response: [checkStatusCode, formatData] }; class EnhancedHttp extends HttpService { constructor(baseURL) { super(baseURL); this.interceptors.request.push(...commonInterceptors.request); this.interceptors.response.push(...commonInterceptors.response); } }3. 中间件模式复杂场景的优雅解耦对于需要处理复杂异步流程的项目中间件模式提供了更好的可维护性和可测试性。3.1 中间件架构设计一个完整的中间件系统应该包含以下要素组件职责示例上下文对象携带请求全过程的数据{ req, res, state }中间件队列按顺序执行的处理器[auth, parse, validate]执行引擎控制中间件流程compose(middlewares)实现代码示例function compose(middlewares) { return function(context, next) { let index -1; function dispatch(i) { if (i index) return Promise.reject(new Error(next() called multiple times)); index i; let fn middlewares[i]; if (i middlewares.length) fn next; if (!fn) return Promise.resolve(); try { return Promise.resolve(fn(context, dispatch.bind(null, i 1))); } catch (err) { return Promise.reject(err); } } return dispatch(0); }; }3.2 典型中间件实现认证中间件async function authMiddleware(ctx, next) { if (!ctx.req.skipAuth) { const token await getToken(); if (!token) { throw new Error(Unauthorized); } ctx.req.header ctx.req.header || {}; ctx.req.header.Authorization Bearer ${token}; } await next(); }日志中间件async function logMiddleware(ctx, next) { const start Date.now(); console.log(请求开始: ${ctx.req.url}); try { await next(); console.log(请求成功: ${ctx.req.url} 耗时: ${Date.now() - start}ms); } catch (err) { console.error(请求失败: ${ctx.req.url}, err); throw err; } }3.3 中间件组合实践将各个中间件组合起来形成完整的处理链const app { middlewares: [], use(fn) { this.middlewares.push(fn); }, async request(config) { const ctx { req: config, res: null }; try { await compose(this.middlewares)(ctx); return ctx.res; } catch (err) { console.error(请求处理失败:, err); throw err; } } }; // 注册中间件 app.use(logMiddleware); app.use(authMiddleware); app.use(async (ctx, next) { ctx.res await wx.request(ctx.req); await next(); }); // 使用示例 app.request({ url: /api/user, method: GET });4. 方案对比与选型指南为了帮助开发者做出合理选择我们整理了三套方案的详细对比4.1 技术特性对比特性全局拦截自定义封装中间件模式侵入性高中低灵活性低高高维护成本高中低调试难度高中低适用规模小型项目中型项目大型项目学习曲线陡峭平缓中等4.2 业务场景适配选择全局拦截当项目规模小所有请求处理逻辑统一需要强制实施某些全局策略没有使用复杂的状态管理选择自定义封装当不同模块需要不同的请求处理项目处于渐进式重构阶段团队对JavaScript原型链理解有限选择中间件模式当系统复杂度高需要清晰的责任划分需要灵活组合各种处理逻辑团队熟悉函数式编程概念4.3 混合使用策略在实际大型项目中我们推荐分层使用这些方案基础层使用轻量全局拦截处理全站统一的逻辑如日志采集服务层为每个业务模块创建自定义Http实例业务层在复杂业务流中使用中间件组合各种处理逻辑示例代码结构/src /http core.js # 基础全局拦截 services/ # 各模块自定义实例 user.js order.js /middlewares # 可复用中间件 auth.js log.js cache.js
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2417608.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!