通常我们使用 axios 库中的 API 设置常用配置以及请求方法封装。更多详情,请查看:axios 常用配置以及请求方法封装。小程序中使用 wx.request API 进行网络请求数据,但是小程序中未对请求提供拦截 API。在 Taro 框架中,我们可以看的 Taro.addInterceptor(callback) API 可以使用拦截器在请求发出前或发出后做一些额外操作。这里的提供的拦截器:请求拦截和响应拦截是没有区分的。
一、请求封装
wx.request 参数说明:
属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 |
---|---|---|---|---|---|
url | string | 是 | 开发者服务器接口地址 | ||
data | string/object/ArrayBuffer | 否 | 请求的参数 | ||
header | Object | 否 | 设置请求的 header,header 中不能设置 Referer。content-type 默认为 application/json | ||
timeout | number | 否 | 超时时间,单位为毫秒 | 2.10.0 | |
method | string | GET | 否 | HTTP 请求方法 | |
dataType | string | json | 否 | 返回的数据格式 | |
responseType | string | text | 否 | 响应的数据类型 | 1.7.0 |
enableHttp2 | boolean | false | 否 | 开启 http2 | 2.10.4 |
enableQuic | boolean | false | 否 | 开启 quic | 2.10.4 |
enableCache | boolean | false | 否 | 开启 cache | 2.10.4 |
success | function | 否 | 接口调用成功的回调函数 | ||
fail | function | 否 | 接口调用失败的回调函数 | ||
complete | function | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
object.method 的合法值:OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT。
通常我们的接口请求主要为 POST 和 GET 请求两种方式。更多详情参数,请查看:Taro 官网之 Taro.request API。
url 为必传参数,data、method、header 为非必传参数。
class HTTP {
request({
url,
data = {},
method = 'GET',
header = {'content-type': 'application/json'}
}) {
return new Promise((resolve, reject) => {
this._request(url, resolve, reject, data, method, header)
}).catch((reject) => {
console.log('catch', reject)
})
}
}
export { HTTP }
之所以要封装一下 wx.request API 的缘由:首先我们请求时,有些接口需要在请求前添加 loading 样式,待到请求结果响应时,将 loading 样式隐藏,同时接口中可能需要添加请求头之类的。
class HTTP {
// data 默认空, method 默认为 GET
_request(url, resolve, reject, data = {}, method = 'GET', header = {'content-type': 'application/json'}) {
let headerParam = {
Authorization: wx.getStorageSync("tokenInfo").token
};
this._show()
Taro.request({
url,
method,
data,
header: {
...header,
...headerParam,
},
success: (res) => {
wx.hideLoading()
if (res.statusCode == 200) {
resolve(res.data)
} else {
reject()
}
},
fail: (err) => {
wx.showToast({
title: '服务器网络错误!',
icon: 'loading',
duration: 1500
})
reject()
}
})
}
_show(){
wx.showLoading({
title: '加载中',
})
}
}
export { HTTP }
在接口定义文档中,我们只需采用 class 的 extends 继承封装的请求方法。
// src/servers/servers.js
import { HTTP } from '../utils/http.js'
const API = {
login: "login/decodeUserInfo", // 登录
......
}
class MineModel extends HTTP {
// 登陆
getLogin(encryptedData, iv, code){
return this.request({
url: API.login,
data: {
encryptedData,
iv,
code,
},
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
})
}
}
export { MineModel }
如何在页面中使用呢?
import { MineModel } from '../../models/mine.js'
const mineModel = new MineModel()
// 请求第三方接口
mineModel.getLogin(res.encryptedData, res.iv, code).then(res => {
console.log('请求第三方接口', res)
})
二、拦截器
在调用 Taro.request 发起请求之前,调用 Taro.addInterceptor 方法为请求添加拦截器,拦截器的调用顺序遵循洋葱模型。
拦截器是一个函数,接受 chain 对象作为参数。chain 对象中含有 requestParmas 属性,代表请求参数。拦截器内最后需要调用 chain.proceed(requestParams) 以调用下一个拦截器或发起请求。
// src/servers/interceptors.js
import { getNewToken } from "./servers";
const customInterceptor = (chain) => {
const requestParams = chain.requestParams;
return chain.proceed(requestParams).then((res) => {
console.log(`http <-- ${url} result:`, res)
return res
// 对返回的信息做处理
})
};
比如 token 过期、请求服务器异常等问题。
Taro 提供了两个内置拦截器 logInterceptor 与 timeoutInterceptor,分别用于打印请求的相关信息和在请求超时时抛出错误。
Taro.addInterceptor(Taro.interceptors.logInterceptor)
Taro.addInterceptor(Taro.interceptors.timeoutInterceptor)
Taro.request({ url })
当然我们可以将拦截器放在一个数组中,代码如下:
const interceptors = [customInterceptor, Taro.interceptors.logInterceptor];
export default interceptors;
在加载请求方式中,采用 forEach 对拦截器中的每一项进行循环添加。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
更新
接口封装:
import Taro from "@tarojs/taro";
import getBaseUrl from "./baseUrl";
class HttpRequest {
baseOptions(params, method = "GET", isShowLoading = true) {
isShowLoading && Taro.showLoading({ title: "加载中", mask: true });
let { url, data, contentType } = params;
let headerParam = {
Authorization: "bearer" + " " + Taro.getStorageSync("tokenInfo")
};
const BASE_URL = getBaseUrl(url);
return new Promise((resolve, reject) => {
Taro.request({
url: BASE_URL + url,
data: data,
method: method,
header: {
"content-type": contentType,
...headerParam
},
success(res) {
isShowLoading && Taro.hideLoading();
resolve(res.data)
},
fail(e) {
const errRes = err.hasOwnProperty("data")
? err.data
: { succeed: false, errorMsg: "请求异常" };
reject(errRes);
}
});
})
}
get(url, data = "", isShowLoading = true, contentType = "application/json") {
let option = { url, data, contentType };
return this.baseOptions(option, isShowLoading);
}
post(url, data, isShowLoading = true, contentType = "application/json") {
let params = { url, data, contentType };
return this.baseOptions(params, "POST", isShowLoading);
}
}
export { HttpRequest }
在接口请求中,getBaseUrl 方法,根据不同的环境,配置不同的请求地址:
const getBaseUrl = () => {
let BASE_URL = "";
if (process.env.NODE_ENV === "development" || || process.env.BUILD_ENV === "dev") { // 开发 测试
BASE_URL = "xxx";
} else if(process.env.BUILD_ENV === "test") { // 测试
BASE_URL = "xxx";
} else if (process.env.BUILD_ENV === "prd") { // 生产
BASE_URL = "xxx";
}
return BASE_URL;
};
export default getBaseUrl;
关于如何配置不同环境的配置,请参考:谈谈 process.env.NODE_ENV。
接下来我们就是如何接口模块化?
// src/service/api/common.js
import { HttpRequest } from '@/service/request'
const URL = {
wxPhoneLogin: '/TALLY/token/wxPhoneLogin.magpie',
}
// 方案一:
class Common extends HttpRequest {
wxPhoneLogin (param) {
return this.post(URL.wxPhoneLogin, param)
}
}
// 方案二:
let request = new HttpRequest()
class Common {
wxPhoneLogin (param) {
return request.post(URL.wxPhoneLogin, param)
}
}
export default new Common()
模块化的好处,我在这里就不说了,那么我们如何使用呢?
// src/service/index.js
import common from './api/common'
export {
common,
}
// 接口调用文件
import { common } from '@/service'
common.wxPhoneLogin({ ...parame })