在小程序中,发起网络请求,都是用小程序提供的 wx.request(OBJECT) 来发起网络请求。
但是在开发的工程中,经常会遇到一个场景:向服务器请求 API 接口时,需要带上一些公共参数,比如
- token
- 版本号
- ...
对于这些公共参数,肯定是不能每次请求都写一遍,而是把它封装到起来。这时候,就需要创建一个工具类
基本信息配置类
网络请求,需要 base url 地址,header 配置等信息,对于这些信息,可以创建一个配置类。
class Config {
constructor() { }
}
Config.restUrl = 'http://z.cn/api/v1/'; // base url
export { Config };
网络请求工具类
有了配置类,就可以着手写网络请求工具类了。
首先是把配置类引入进来,并在构造函数中进行一些初始化
import { Config } from 'config.js';
class Base {
constructor() {
this.baseRequestUrl = Config.restUrl;
}
}
对于一个网络请求,需要以下信息:
- url
- http 请求方法(GET 还是 POST 甚至其他方法)
- 请求参数
- 请求成功时的回调
- 请求失败时的回调
对于这些信息,我们可以把它封装在一个对象中,比如:
{
type: 'post',
data: {
'id': 1,
'name': 'xxx'
},
sCallback: function() {
},
eCallback: function() {
}
}
剩余的工作就由工具类来完成了。
request(params) {
var url = this.baseRequestUrl + params.url;
if (!params.type) {
// 如果 type 不填写,默认是 GET
params.type = 'GET';
}
wx.request({
url: url,
data: params.data,
method: params.type
success: function (res) {
var code = res.statusCode.toString();
var startChar = code.charAt(0);
if (startChar == '2') {
// 对于 2xx 返回码
params.sCallback && params.sCallback(res.data);
}
else {
// 对于 其他返回码
params.eCallback && params.eCallback(res.data);
}
},
fail: function (err) {
console.log(err);
}
})
}
使用方法
比如,我正在做商品分类界面(pages/category/category)。于是创建一个 category-model.js 文件作为模型层,类的定义如下
import { Base } from '../../utils/base.js';
class Category extends Base {
constructor() {
super();
}
/**
* 获得所有分类
*/
getCategoryType(callback) {
var param = {
url: 'category/all',
sCallback: function (data) {
callback && callback(data);
}
};
this.request(param);
}
}
export { Category };
我所需要写的代码,只需要继承 Base 类。
class Category extends Base
定义一下访问的 url 和回调函数
var param = {
url: 'category/all',
sCallback: function (data) {
callback && callback(data);
}
};
然后发起网络请求即可
this.request(param);
然后在 category.js 中调用方法,绑定数据到界面上即可
category.getCategoryType((categoryData) => {
this.setData({
categoryTypeArr: categoryData
});
};
token 管理
网络请求,如果遇到需要 token 的接口,请求时需要带上 token 才能调用。对于 token,另外写一个工具类
import { Config } from 'config.js';
class Token {
constructor() {
this.verifyUrl = Config.restUrl + 'token/verify';
this.tokenUrl = Config.restUrl + 'token/user';
}
/**
* 调用 API 接口,校验 token 是否有效
*/
verify() {
var token = wx.getStorageSync('token');
if (token) {
// 存在,就向服务器校验token
this._veirfyFromServer(token);
} else {
// 不存在,就去服务器请求token
this.getTokenFromServer();
}
}
/**
* 请求API接口,校验token的合法性
* 如果不合法,会自动调用 getTokenFromServer 方法请求 token
*/
_veirfyFromServer(token) {
var that = this;
wx.request({
url: that.verifyUrl,
method: 'POST',
data: {
token: token
},
success: function (res) {
var valid = res.data.isValid;
if (!valid) {
that.getTokenFromServer();
}
}
})
}
/**
* 请求API接口,获取新的token
*/
getTokenFromServer(callBack) {
var that = this;
wx.login({
success: function (res) {
// 既然时一个工具类,就应该存粹一点,不要用 base.js 里的 request(params) 方法发起网络请求了
// 这一点很重要
wx.request({
url: that.tokenUrl,
method: 'POST',
data: {
code: res.code
},
success: function (res) {
wx.setStorageSync('token', res.data.token);
callBack && callBack(res.data.token);
}
})
}
})
}
}
export { Token };
使用 token 时,有几个种情况
- 请求 API 接口时,token 是有效的
- 请求 API 接口时,token 失效了
对于 token 有效的情况,不做任何处理。而对于 token 失效的情况,需要重新获取 token,并重新调用 api 接口。不过“重新获取 token,并重新调用 api 接口”这个操作不能重复获取,需要对他进行以下限制,比如限制重试次数等。
于是,改善以下网络请求工具类,加入对 token 失效时的处理操作。只需要判断以下 http 的返回码,对不同的返回码做处理即可
// 当noRefech为true时,不做未授权重试机制
request(params, noRefetch) {
// ...
wx.request({
// ...
header: {
'token': wx.getStorageSync('token') // 带上 token
},
success: function (res) {
var code = res.statusCode.toString();
var startChar = code.charAt(0);
if (startChar == '2') {
params.sCallback && params.sCallback(res.data);
}
else {
if (code == '401') {
// token.getTokenFromServer
// base.request
if (!noRefetch) {
that._refetch(params);
}
}
if (noRefetch) {
params.eCallback && params.eCallback(res.data);
}
}
},
// ...
})
}
_refetch(params) {
var token = new Token();
token.getTokenFromServer((token) => {
this.request(params, true);
});
}
完整代码
import { Config } from 'config.js';
import { Token } from 'token.js';
class Base {
constructor() {
this.baseRequestUrl = Config.restUrl;
}
// 当noRefech为true时,不做未授权重试机制
request(params, noRefetch) {
var that = this;
var url = this.baseRequestUrl + params.url;
if (!params.type) {
params.type = 'GET';
}
wx.request({
url: url,
data: params.data,
method: params.type,
header: {
'content-type': 'application/json',
'token': wx.getStorageSync('token')
},
success: function (res) {
// if(params.sCallBack){
// params.sCallBack(res);
// }
var code = res.statusCode.toString();
var startChar = code.charAt(0);
if (startChar == '2') {
params.sCallback && params.sCallback(res.data);
}
else {
//AOP
if (code == '401') {
// token.getTokenFromServer
// base.request
if (!noRefetch) {
that._refetch(params);
}
}
if (noRefetch) {
params.eCallback && params.eCallback(res.data);
}
}
},
fail: function (err) {
console.log(err);
}
})
}
_refetch(params) {
var token = new Token();
token.getTokenFromServer((token) => {
this.request(params, true);
});
}
/*获得元素上的绑定的值*/
getDataSet(event, key) {
return event.currentTarget.dataset[key];
};
}
export { Base };
总结
封装相同的、不变的到上层中去,抽离变化的到下层中来。