在微信小程序中创建一个统一的网络请求工具类

在小程序中,发起网络请求,都是用小程序提供的 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 };

总结

封装相同的、不变的到上层中去,抽离变化的到下层中来。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 5,160评论 1 23
  • 保监会从今日起,对安邦集团实施接管,接管期一年。 原董事长、总经理吴小晖,被依法提起公诉。 资本在崭露...
    我是张小荷阅读 102评论 0 0
  • (接上篇) GC和TB相比,二者属于完全不一样的领域,GC身处云计算,TB属于移动互联网。如果说TB是身处移动互联...
    zoe相信未来阅读 851评论 0 51
  • 写作大道,无关文字。 非惊世骇俗,故作奇语,有感于贵专栏稿件《我为什么写作》而已。作者中文系毕业,谦称不懂写作。幽...
    陈茀茀阅读 410评论 0 0