如果你用 Taro 开发微信小程序,并且在发起网络请求前使用了拦截器(包括 Taro 官方提供的两个内置拦截器),就有可能会遇到 RequestTask 对象方法丢失的问题,例如:
TypeError: res.onHeadersReceived is not a function
// 或者
TypeError: res.onChunkReceived is not a function
如果没有使用拦截器,就不会出现上面的问题,RequestTask 中的方法可以正常调用。
原因是拦截器里返回的不是原始的 RequestTask 对象,而是一个新的对象。这个新对象中没有 onHeadersReceived 这些方法。
问题的具体原因
const interceptor = function (chain) {
const requestParams = chain.requestParams
const { method, data, url } = requestParams
console.log(`http ${method || 'GET'} --> ${url} data: `, data)
return chain.proceed(requestParams)
.then(res => {
console.log(`http <-- ${url} result:`, res)
return res
})
}
上面这个是 Taro 官网中给出的自定义拦截器示例。
这个拦截器的返回值是调用了 then() 方法后生成的全新的 Promise 对象,当然不会包含 RequestTask 中的方法。
如何解决
解决方法比较简单,要么返回原始的 RequestTask 对象,要么自己重新挂载一次 RequestTask 的方法。
返回原始的 RequestTask 对象
还是引用官方的示例,改成下面这样,就能在后续的代码中调用 RequestTask 对象的方法:
const interceptor = function (chain) {
const requestParams = chain.requestParams
const { method, data, url } = requestParams
console.log(`http ${method || 'GET'} --> ${url} data: `, data)
const task = chain.proceed(requestParams)
task.then((res) => {
console.log(`http <-- ${url} result:`, res)
return res
})
return task
}
通过查看 Taro.request() 和 chain.process() 方法的源码,会发现 chain.proceed(requestParams) 的返回值已经挂载了微信小程序原生 RequestTask 对象的方法。所以如果拦截器里返回的是 then() 方法的返回值,就会丢失 RequestTask 对象的方法。
Taro.request() 的源码在 packages/shared/src/native-apis.ts 文件,
chain.process() 的源码在 packages/taro-api/src/interceptor/chain.ts 文件。
重新挂载 RequestTask 中的方法
Taro 内置的拦截器也有类似的实现,不过只挂载了 abort() 方法。例如 logInterceptor() 的实现:
export function logInterceptor (chain: Chain) {
const requestParams = chain.requestParams
const { method, data, url } = requestParams
// eslint-disable-next-line no-console
console.log(`http ${method || 'GET'} --> ${url} data: `, data)
const p = chain.proceed(requestParams)
const res = p
.then(res => {
// eslint-disable-next-line no-console
console.log(`http <-- ${url} result:`, res)
return res
})
// 这里复制了 abort 方法
if (isFunction(p.abort)) res.abort = p.abort
return res
}
所以,如果要使用 RequestTask 中的方法,可以仿照 logInterceptor() 的实现在自定义的拦截器返回之前,加上一句:
if (isFunction(p.onHeadersReceived)) res.onHeadersReceived = p.onHeadersReceived
不过需要注意,由于官方内置的拦截器没有挂载其他 RequestTask 方法,所以如果你使用了官方内置的拦截器,后续的代码依然获取不到 onHeadersReceived() 等方法。有需要的话可以用自定义的拦截器代替。