js 封装经纬度成json_【微信小程序】- 封装wx.request请求

起因:

接手修改并维护一个“微信小程序”项目,看了下整个项目的代码,发现写的比较随意,可维护性差,所有的接口请求全都是用wx.request()写的,没有进一步封装,没统一捕获异常和错误提醒了。本着基于原有的代码封装request并最小程度的改之前所有的wx.request()代码。

原有代码:(看着就头疼,强迫症的估计都看不下去,所有的接口都是这样写,都不闲累的...)

其一:(success回调http状态200之类的执行)

                           wx.request({
				url: app.globalData.http + '/某接口地址',
				header: {
					'Api-Ext': app.globalData.apiExt
				},
				data: {
					page: page
				},
				dataType: 'json',
				method: 'GET',
				success: function (data) {
					if (data.statusCode >= 200 && data.statusCode < 300) {
						if (params === 'isConcat' && data.data.length > 0) {
							let tempArr = that.data.good
							let newArr = tempArr.concat(data.data)
							that.setData({
								good: newArr
							})
						} else if (params === 'isConcat' &&  data.data.length == 0) {
							let newPage = that.data.currentPage
							newPage--
							that.setData({
								currentPage: newPage
							})
						} else {
							that.setData({
								good:data.data
							})
						}
						resolve()
					} else {
						reject()
					}
				},
				fail: function (data) {
					reject()
				},
                                complete() {
                                        wx.hideLoading()
                                }
			})

其二:(success回调需要通过statusCode来进行不同逻辑处理的代码)

      wx.request({
        url: app.globalData.http + '/某接口地址',
        method: 'GET',
        dataType: 'json',
        header: {
          "Api-Key": app.globalData.apiKey,
          "Api-Secret": app.globalData.apiSecret,
          'Api-Ext': app.globalData.apiExt
        },
        success: function (data) {
          if (data.statusCode == 200) {
            that.setData({
              info: data.data,
              pageVisible: true
            })
            if (data.data.status == 200) {
              var time = data.data.expire_at
              that.setInterval(time)
            }
          } else {  // 这边是要通过statusCode来进行不同逻辑处理的代码
            wx.showToast({
              title: '订单不存在',
              icon: 'none',
              duration: 3000
            })
            that.setData({
              pageVisible: false
            })
          }
        },
        complete() {
          wx.hideLoading()
        }

从上面的代码我们可以发现几点不合理的地方:

  1. 每次都带同一个header: { 'Api-Ext': app.globalData.apiExt }。
  2. url每次都拼接同一个域名:url: app.globalData.http + '/某接口地址'。
  3. 多写dataType: 'json',json格式是wx.request()默认的。
  4. 多写method: 'GET',也是默认的。
  5. success回调里面每次都判断接口是否200,if (data.statusCode >= 200 && data.statusCode < 300){}。
  6. 非200的接口没提醒,用户一接口异常,就一脸懵逼啥提醒都没。
  7. 没做fail回调的错误提醒。
  8. 每次都写关闭loading的代码: complete() {wx.hideLoading()}。

基于以上几点问题就自己尝试着封装一个request.js

改动后的代码:

request.js:

// app.js中获取不到getApp()
let globalData = '' // 接口domain

// 成功http状态码
const successCodes = [200, 201, 202, 204, 304]

// http状态码对应的默认文本提醒
const codeMessage = {
    200: '操作成功',
    201: '新建或修改数据成功',
    202: '一个请求已经进入后台排队(异步任务)',
    204: '删除数据成功。',
    400: '参数错误',
    401: '需要用户验证',
    403: '用户无权限',
    404: '资源不存在',
    405: '不支持的操作方法',
    406: '请求的格式不可得。',
    410: '请求的资源被永久删除,且不会再得到的',
    422: '当创建一个对象时,发生一个验证错误',
    500: '服务器内部错误',
    502: '应用程序错误',
    503: '维护中',
    504: '网关超时',
}

// 请求的默认参数
const defaultOptions = {
    method: 'GET',
    success: function(){},
    fail: function(err){
        console.log(err)
        _showToast("网络错误")
    },
    complete: function(){},
}

// 默认的额外参数
const defaultOtherOptions = {
    useSuccessCode: true, // 是否检测预定义的http请求的成功状态
    isShowError: true, // 是否显示错误提醒
    globalData: '',
}

// 返回Promise的微信请求对象,(wx.request返回的不是Promise)
function _wxRequest(options) {
    return new Promise((resolve, reject) => {
        try {
            wx.request({
                ...options,
                success: function(...arg) {
                    resolve(...arg)
                },
                fail: function(...arg) {
                    reject(...arg)
                },
            })
        }catch (e) {
            console.log(e)
            reject(e)
        }
    })
}

// toast提醒函数
function _showToast(title = '网络请求失败', duration = 3000) {
    wx.showToast({
        title: title,
        icon: 'none',
        duration: duration
    })
}

// 导出
export default async function(fetchOptions = {header: {}}, otherOptions) {

    // 合并otherOptions
    otherOptions = typeof otherOptions !== 'undefined' ? {...defaultOtherOptions, ...otherOptions} : defaultOtherOptions

    // app.js中获取不到getApp(), globalData需要传进来
    if(otherOptions.globalData) { // app.js传globalData
        globalData = otherOptions.globalData
    }else { // 非app.js不传
        const app = getApp()
        globalData = JSON.parse(JSON.stringify(app.globalData))
    }

    // 设置header统一的Api-Ext属性
    defaultOptions.header = {'Api-Ext': globalData.apiExt}

    // 合并newFetchOptions
    const newHeader = {...defaultOptions.header, ...fetchOptions.header}
    const newFetchOptions = {...defaultOptions, ...fetchOptions}
    newFetchOptions.header = newHeader
    newFetchOptions.url = `${globalData.http}${newFetchOptions.url}`
    
    // 等异步wx.request()返回结果
    const requestResult = await _wxRequest(newFetchOptions)
    const { errMsg } = requestResult

    // 真机wx.toast和showLoading只能同时允许一个出现,故请求完成全部关闭,以便后续能toast提醒
    wx.hideLoading()

    // 请求网络成功
    if(errMsg.includes('ok')) {

        // 使用预设的成功状态
        if(otherOptions.useSuccessCode) {
            const { statusCode, data } = requestResult
            if( successCodes.findIndex((item) => item === statusCode) > -1) { // 成功code,返回requestResult.data和requestResult
                newFetchOptions.success(data, requestResult)
            }else { // 提醒错误,不返回requestResult
                console.log('非请求成功状态', requestResult)
                let msg = codeMessage[statusCode] || '未知错误'
                if(typeof data !== 'undefined' && typeof data.message !== 'undefined') {
                    msg = data.message
                }
                otherOptions.isShowError && _showToast(msg)
            }
        }else{ // 不使用预设的成功状态, 返回整个requestResult
            console.log('不使用预设的成功状态', requestResult)
            newFetchOptions.success(requestResult)
        }

    } else { // 请求网络失败
        otherOptions.isShowError && _showToast()
        newFetchOptions.fail(requestResult)
    }

    // 执行complete函数
    newFetchOptions.complete()

}

request.js的注意点:

  1. 接口domain,由于app.js中调用不到getApp()实例,就没有这样写:
const app = getApp()
const globalData = app.globalData.http

改成这样写:app.js中调用request,额外传入其他参数:globalData,其他js中直接调用getApp()取到:

    // app.js中获取不到getApp(), globalData需要传进来
    if(otherOptions.globalData) { // app.js传globalData
        globalData = otherOptions.globalData
    }else { // 非app.js不传
        const app = getApp()
        globalData = JSON.parse(JSON.stringify(app.globalData))
    }

2. 等异步wx.request()返回结果,直接关闭wx.hideLoading();因为小程序中wx.showToast, wx.showLoading, 同时只能出现一个;wx.hideLoading()都会关闭2者的土司弹窗。也就不用每次request的时候都写complete来关闭弹窗:如:

  complete() {
     wx.hideLoading()
  }

3. 设计其他参数:otherOptions.useSuccessCode的用意是:有的请求需要通过statusCode来处理不同的逻辑,就返回(response),正常成功请求的返回(data, response)(返回data是为了success中少写点代码:如:data = response.data; 返回response以防万一需要用到response)。

4. 函数开始得先合并请求参数newFetchOptions,把header中相同的“Api-Ext”写入,并把请求的url合并起来:newFetchOptions.url = `${globalData.http}${newFetchOptions.url}`。

5. wx.request请求是异步的,我们通过async/await来同步书写代码。把wx.request封装起来,返回Promise对象。并在函数内部把wx.request可能出现的错误捕获掉(其实这个try/catch可以不用捕获的,wx.request发生错误都捕获到fail()里了)。

function _wxRequest(options) {

    // 由于wx.request不是返回Promise对象,我们要自己包一层Promise
    return new Promise((resolve, reject) => {
        try {
            wx.request({
                ...options,
                success: function(...arg) {
                    resolve(...arg)
                },
                fail: function(...arg) {
                    reject(...arg)
                },
            })
        }catch (e) {
            console.log(e)
            reject(e)
        }
    })
}

const requestResult = await _wxRequest(newFetchOptions)

6. 非成功状态码的文本提醒,如果接口有返回提醒,则用接口的提醒,否则用messageCode中的提醒:

                console.log('非请求成功状态', requestResult)
                let msg = codeMessage[statusCode] || '未知错误'
                if(typeof data !== 'undefined' && typeof data.message !== 'undefined') {
                    msg = data.message
                }
                otherOptions.isShowError && _showToast(msg)

调用request.js的写法:

                       import request from '@Utils/request'

                       // 写法1:http状态码:200处理data, 成功回调的返回success(data, res)
                       request({
				url: '/mpa/cart/...',
				success(data, res) {
					that.setData({
						cartNum: data
					})
				}
			})

                        // 写法2:通过statusCode处理不同逻辑,传入额外参数:{ useSuccessCode: false },成功回调的返回success(res)
                        request({
				url: '/mpa/goods/...',
				data: {
					keywords: keyName
				},
				success(res) { // 这里就返回res
					var code = res.statusCode.toString()
					if (code == 500) {
						that.setData({
							dataList: []
						})
					} else if (code.indexOf('40') > -1) {
						var tip = res.data.message.toString()
						wx.showToast({
							title: tip,
							icon: 'none',
							duration: 1000
						})
					} else {
						let tempArr = res.data
						tempArr.map(item => {
							let string = that.getHilightStrArray(item.name, keyName)
							item.newStringArr = string
							return item
						})
						that.setData({
							dataList: tempArr,
							keyName: keyName
						})
					}
				}
			}, { useSuccessCode: false })


                        // 写法3:在app.js中调用request,得额外传入globalData
                        request({
				url: '/mpa/.../...',
				success(data, res) {
					that.globalData.distribution = data
					resolve()
				},
				fail(res) {
				        reject(res)
				}
			}, { globalData: that.globalData })

相比之前的wx.request代码,简洁多了,而且请求一目了然,错误code都有统一提醒; 我们只需要刪除原有多余的代码就好,而不用去改原有代码的写法。做到更低代码量的重构改动。


总结:

重构思想:

  1. 能复用的要尽量复用,少写多余重复的代码。
  2. 要多多考虑到边界情况:如:otherOptions.isShowError是否要显示错误的toast弹窗;接口没有返回错误的message,let msg = codeMessage[statusCode] || '未知错误',补足msg;预留额外参数:otherOptions。等等...
  3. 要考虑到多种调用场景或情况。如:app.js中调用;success中通过statusCode执行不同逻辑。
  4. 代码顺序最好用es6后的新语法来同步书写异步代码,命名尽量语义化,多写备注,才能一目了然,毕竟你的代码后面也会有人接手的。如: if(errMsg.includes('ok'))一看就知道是请求成功的情况。
  5. 尽量封装好一些默认的行为,如:
function _showToast(title = '网络请求失败', duration = 3000) {
    wx.showToast({
        title: title,
        icon: 'none',
        duration: duration
    })
}
const defaultOptions = {
    method: 'GET',
    success: function(){},
    fail: function(err){
        console.log(err)
        _showToast("网络错误")
    },
    complete: function(){},
}

要想改动原有项目大量的代码,最好重构到不破坏原有代码的结构去精简代码(而且不是你自己的写项目,代码改多了,可能会出现很多意想不到的结果,反而得花更多的时间去排查错误),就这个request.js里面的坑还蛮多的;比如:

wx.request()返回的不是Promise对象;

app.js中获取不到getApp()实例,globalData就得额外传入;

真机wx.toast和showLoading只能同时允许一个出现,故请求完成全部关闭,以便后续能toast提醒;

小程序真机调试中:400,500等的httpCode都不会报异常,而且所有的异常都会阻塞代码进程,错误异常记得捕获。真机调试非常的头疼,可多写log输出。(调试时间花费了非常多...)

改这个也是头疼......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值