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

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

总结

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

你可能感兴趣的:(在微信小程序中创建一个统一的网络请求工具类)