基于 wx.request 封装类似axios的拦截器

该博客介绍了如何在微信小程序的开发过程中,由于wx.request不支持axios样式的请求和响应拦截,作者选择自定义一个HttpClient类来实现这一功能。通过定义请求方式、请求入参类以及请求和响应拦截器,作者创建了一个完整的HTTP客户端,包括GET、HEAD、POST等请求方法,并提供了使用示例。同时,还展示了如何设置请求拦截器(如添加授权头)和响应拦截器(如处理不同状态码的响应)。

在开发微信小程序的过程中,因为在接口发送请求需要对请求入参进行统一加密,但是wx.request没有提供类型axios的请求拦截和响应拦截方式,所以觉得自己封装一个。

// 请求方式
export enum HttpMethod {
  GET = "GET",
  HEAD = "HEAD",
  POST = "POST",
  PUT = "PUT",
  DELETE = "DELETE",
  OPTIONS = "OPTIONS",
  TRACE = "TRACE",
  CONNECT = "CONNECT"
}

/// 请求入参
class RequestMessage {
  Api:string;
  Param: any;
  Method: HttpMethod;
  Header: Object;
  constructor(api:string,param:any,method:HttpMethod = HttpMethod.GET,header:any = null){
    this.Api = api;
    this.Param = param;
    this.Method = method;
    this.Header = header;
  }
}

/**
 * HttpClient 实例
 */
class HttpClient {
  private RequestInterceptor: Function | undefined;
  private ReponseInterceptor: Function | undefined;
  private baseUrl: string;
  constructor(baseUrl:string = ''){
    this.baseUrl = baseUrl;
  }
  
  /** 请求拦截器 */
  public UseRequestInterceptor(interceptor:(requestMessage: RequestMessage) => boolean): HttpClient {
    this.RequestIntercept
<think>我们讨论的是在原生微信小程序中,在不封装wx.request的前提下,如何实现全局拦截wx.request的后置处理(例如统一处理响应、错误等)。由于微信小程序没有提供类似axios拦截器机制,所以需要一些技巧来实现。 思路: 1. 由于不能封装wx.request,我们不能直接修改wx.request的调用方式。 2. 但是我们可以通过覆盖wx.request方法来实现拦截。我们可以在app.js中重写wx.request,在重写的方法中,先执行一些前置操作,然后调用原始的wx.request,并在其success、fail、complete回调中插入我们的后置处理逻辑。 步骤: - 在app.js的onLaunch中,保存原始的wx.request方法。 - 用一个新的函数覆盖wx.request,这个新函数接收的参数和原始wx.request一样(一个配置对象)。 - 在新的函数中,我们调用原始的wx.request,并传入配置对象,但是我们要改写这个配置对象的success、fail、complete回调函数,以便在这些回调中插入我们的全局后置逻辑。 注意: 1. 我们需要确保在调用原始的wx.request时,传入的success、fail、complete是我们封装过的,但是同时也要执行用户传入的回调。 2. 我们也要注意,如果用户同时传入了success和complete,我们的拦截逻辑要确保都能执行,并且顺序合理(success/fail之后执行complete,然后执行我们的全局后置逻辑?实际上,微信小程序中complete总是会执行,所以我们可以在complete中进行全局后置处理,这样无论成功失败都会执行)。 3. 但是,我们可能需要在success和fail中分别做一些处理,例如根据成功或失败执行不同的拦截逻辑。 具体实现: 我们可以选择在complete回调中做全局后置处理,因为无论成功失败,complete都会执行。但是,如果我们想要在success和fail中做一些不同的处理,那么我们可以分别拦截success和fail,然后在其中加入我们的逻辑,最后再调用用户传入的回调。 例如: 在app.js中: ```javascript App({ onLaunch() { // 保存原始request方法 const originalRequest = wx.request; // 覆盖wx.request wx.request = function (config) { // 用户传入的成功回调 const userSuccess = config.success; // 用户传入的失败回调 const userFail = config.fail; // 用户传入的完成回调 const userComplete = config.complete; // 重新设置success回调 config.success = function (res) { // 先执行全局后置成功处理(比如根据状态码判断,这里可以统一处理一些业务逻辑,例如token过期等) console.log('全局成功拦截', res); // 执行用户传入的成功回调 if (typeof userSuccess === 'function') { userSuccess(res); } }; config.fail = function (err) { // 全局失败拦截 console.log('全局失败拦截', err); if (typeof userFail === 'function') { userFail(err); } }; config.complete = function (res) { // 这里可以做一些无论成功失败都要做的操作,比如隐藏loading等 console.log('全局完成拦截', res); if (typeof userComplete === 'function') { userComplete(res); } }; // 调用原始的request方法 originalRequest(config); }; } }); ``` 但是,这样写有一个问题:如果我们在同一个页面同时发起多个请求,那么每个请求都会触发这些拦截器。而且,这样覆盖的方式可能会引起一些难以预料的问题,因为wx.request被全局替换了。 另外,注意这种方式并没有使用Promise,而是使用回调函数。微信小程序的wx.request本身不支持Promise(除非我们自己用Promise封装,但题目要求不封装wx.request)。 然而,这种方法实际上是封装wx.request,只不过是在全局覆盖的方式下。题目要求“不封装wx.request”,但如果不覆盖wx.request,我们无法实现全局拦截。所以,这里我们只能理解为不要求每个请求单独封装,而是通过全局覆盖的方式实现拦截。 但是,上面的写法有一个潜在的问题:如果我们在覆盖之前已经有一些地方调用了wx.request,那么覆盖后这些地方的调用也会被拦截。因此,我们可以在覆盖之前先保存原始引用,然后在覆盖后的函数中调用原始引用。 另外,我们还要考虑到,微信小程序中可能会有其他插件或库也覆盖了wx.request,所以我们的覆盖应该是在最外层,并且保留原始的方法。 改进:我们可以将覆盖放在app.js的最开始,但是注意,app.js的onLaunch是小程序启动时执行的,所以在这之前调用的wx.request不会受到覆盖的影响(因为在小程序启动之前,页面和组件中的代码不会执行)。所以一般情况下,在onLaunch中覆盖是安全的。 但是,如果有一些代码在app.js的onLaunch之前执行并调用了wx.request(比如在app.js的顶部直接调用),那么这些调用就不会被拦截。因此,我们应该确保在覆盖之前没有wx.request调用。 另一种做法:我们可以将覆盖的代码放在app.js的最顶部(在onLaunch之前),但是这样就要确保在覆盖之前没有wx.request调用。通常,我们建议在onLaunch中覆盖。 总结:通过覆盖wx.request方法,我们在每个请求的回调中添加了全局拦截逻辑,实现了后置拦截。 但是,请注意,这种方法可能会对性能产生一定影响,因为每次请求都要经过我们的拦截函数。不过,对于小程序来说,这个开销是可以接受的。 另外,我们也可以考虑在complete回调中统一处理,这样就不需要分别处理success和fail。例如: ```javascript config.complete = function (res) { // 在这里根据res的状态来判断成功或失败,然后做统一处理 if (res.statusCode >= 200 && res.statusCode < 300) { // 成功 console.log('请求成功', res); } else { // 失败 console.log('请求失败', res); } // 然后执行用户的complete if (typeof userComplete === 'function') { userComplete(res); } }; ``` 这样,我们就可以在一个地方处理所有请求的完成情况。 根据需求选择合适的方式。 最后,我们还需要注意错误处理,避免因为拦截器中的错误导致用户传入的回调不被执行。因此,我们可以用try-catch包裹我们自己的拦截代码。 示例代码(使用complete统一处理): ```javascript App({ onLaunch() { const originalRequest = wx.request; wx.request = function (config) { const { success, fail, complete } = config; config.success = function(res) { try { // 这里可以执行一些针对成功响应的操作 // 比如根据状态码判断业务逻辑的成功与失败,因为有时http状态码是200但业务逻辑可能是失败的 // 例如,如果后端返回的状态码为401,则跳转到登录页 if (res.statusCode === 401) { // 跳转到登录页 wx.navigateTo({ url: '/pages/login/login' }); } } catch (e) { console.error('全局拦截器success处理异常', e); } if (typeof success === 'function') { success(res); } }; config.fail = function(err) { // 网络错误等 try { console.error('请求失败', err); // 可以统一显示错误提示 wx.showToast({ title: '网络错误,请重试', icon: 'none' }); } catch (e) { console.error('全局拦截器fail处理异常', e); } if (typeof fail === 'function') { fail(err); } }; config.complete = function(res) { try { // 无论成功失败,都会执行 console.log('请求完成', res); } catch (e) { console.error('全局拦截器complete处理异常', e); } if (typeof complete === 'function') { complete(res); } }; originalRequest(config); }; } }); ``` 以上代码中,我们在success中处理了401跳转登录,在fail中统一提示网络错误,在complete中打印日志。注意,这里我们分别处理了success、fail和complete,并且调用了用户传入的回调。 但是,这里有一个问题:当http状态码为401时,我们跳转到了登录页,并且还会执行用户传入的success回调(因为401的请求也会触发success回调,因为微信小程序中只要成功收到服务器的响应就会触发success,无论状态码是多少)。所以,我们可能需要在success中根据状态码判断业务逻辑。 因此,更常见的做法是在success中根据状态码进行业务拦截,比如状态码为200且业务状态码为特定值时表示成功,其他情况视为业务错误。 如果我们想在全局处理所有非200状态码(比如跳转登录),那么可以在success中判断res.statusCode,如果是401则跳转登录,并且可以选择不执行用户传入的success(因为已经出错,不需要再执行业务逻辑)。但是这样可能会影响原有逻辑,所以需要谨慎。 所以,具体实现要根据业务需求来定。 最后,关于引用:由于用户提供了引用[1][2][3],但在这个问题中并没有直接相关的引用,所以不需要在回答中标注引用。但是,如果用户提供的引用中有相关内容,我们可以参考。从引用中我们并没有看到与拦截wx.request相关的内容,因此我们的回答主要基于微信小程序的机制和常见的拦截器实现方式。 相关问题: 1. 微信小程序中如何实现类似axios的请求拦截器? 2. 在微信小程序中如何统一处理未登录状态? 3. 覆盖原生API的方法有哪些潜在风险?如何避免? 以上。 </think>### 原生微信小程序实现全局拦截 wx.request 后置处理的方法 在原生微信小程序中,若不封装`wx.request`实现全局拦截后置处理,可以通过以下两种核心方法实现: --- #### 方法一:通过全局 Promise 封装(推荐) **核心思路**:在 `app.js` 中创建全局 Promise 封装逻辑,但不修改原始 `wx.request` 调用方式。 ```javascript // app.js App({ onLaunch() { // 创建全局后置处理方法 this.requestPostInterceptor = (res, originalOptions) => { console.log('全局后置处理', res); // 示例1: 统一处理状态码 if (res.statusCode === 401) { wx.navigateTo({ url: '/pages/login/login' }) } // 示例2: 统一处理服务器返回格式 if (res.data?.code !== 200) { wx.showToast({ title: res.data?.msg || '请求异常' }) } return res; // 返回结果给原始调用 }; } }); // 页面中使用 const originalRequest = wx.request; const options = { url: 'https://api.example.com/data', success: (res) => { // 调用全局后置拦截 const processedRes = getApp().requestPostInterceptor(res, options); // 继续原始业务逻辑 console.log('业务处理', processedRes); } }; originalRequest(options); ``` **优势**: 1. 完全保留 `wx.request` 原生调用方式 2. 全局统一处理响应数据、错误码和跳转逻辑 3. 通过返回 `res` 保持链式调用[^1] --- #### 方法二:重写 Page 生命周期(适用于页面级拦截) **核心思路**:在全局 `Page` 构造函数中注入后置处理逻辑 ```javascript // app.js const originalPage = Page; Page = function(config) { const originalOnLoad = config.onLoad || function() {}; config.onLoad = function(options) { // 在页面加载时注入请求后置处理 this.$requestPostProcess = (res) => { console.log('页面级后置处理', res); // 示例:Token刷新 if (res.data?.code === 10001) { getApp().refreshToken(); return true; // 标记需要重试 } return false; }; originalOnLoad.call(this, options); }; originalPage(config); }; // 页面中使用 wx.request({ url: 'https://api.example.com/user', success: (res) => { if (this.$requestPostProcess(res)) { // 若返回true则重试请求 wx.request({ /* 原始参数 */ }) } else { // 正常业务处理 this.setData({ user: res.data }) } } }); ``` **适用场景**: - 需要页面级定制拦截逻辑 - 需要实现自动重试机制 - 需要结合页面生命周期管理请求[^3] --- ### 关键注意事项 1. **错误处理完整性**: ```javascript // 必须拦截 fail 和 complete wx.request({ fail: (err) => { getApp().requestPostInterceptor({ err }, options); } }); ``` 2. **性能优化**: - 避免在拦截器中执行同步阻塞操作 - 使用 `try-catch` 包裹拦截逻辑防止崩溃 3. **内存管理**: ```javascript // 页面卸载时清理引用 Page({ onUnload() { this.$requestPostProcess = null; } }) ``` --- ### 方案对比 | 方法 | 优点 | 缺点 | 适用场景 | |------------|--------------------------|-----------------------|-----------------------| | 全局Promise| 完全零入侵原生API | 需手动调用拦截器 | 简单全局拦截 | | 重写Page | 自动注入页面生命周期 | 修改框架基础行为 | 复杂页面级逻辑 |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值