微信小程序开发——前端如何区分小程序运行环境

前言:

之前用vue做h5项目,对于接口请求,都是根据前端访问域名来判断运行环境,然后自动适配对应的服务器地址的。这样的好处就是在开发、测试及发布上线全程都不需要手动去改接口请求地址,只要提前配置好就行了。这样处理之后,只需要打包一次,就能同时适应所有环境而不需要再去改代码,打不同的包了。

对于微信小程序,发现前端并没有可以区分小程序运行环境(开发者工具、开发版、体验版及正式版)的API(真的没有),这就直接导致了开发的时候链接测试服务器就需要手动的去修改服务器地址了。最近找到一种解决方法,实现上虽然还是有点曲折,但是总算能解决问题了,也希望腾讯后边能开发这方面的API。

实现原理:

如图,小程序网络请求的请求头中的Reffer为固定格式:

Referer:https://servicewechat.com/小程序的appid/运行环境/page-frame.html

经验证,开发者工具中,为devtools,开发版及体验版为0,正式版则为1,这样就能区分运行环境了。

但是这个请求头通过前端并不能获取,所以只有让后端在第一个接口请求中获取referer,然后返回给前端就好了。

实现步骤:

1. 后端将接口访问请求头中的reffer返回给前端:

注:只需要在小程序第一个接口(必须访问)中将reffer返回给前端就好了,如果第一个接口不一定访问,那么可以让后端单独开放一个接口给前端来判断即可。

2. 前端对接口请求封装代码进行改造,如图:

代码解析:

贴下代码,加粗字体为修改部分:

/**
 * 封装http请求方法,已实现根据访问环境自动匹配服务器环境,原理详见README.md
 */
var apiUrl = "https://xxx.xxx.cn"; //生产环境
var apiUrlDev = "http://xxx.xxx.cn"; //测试环境
//优先设置为缓存中的服务器地址
var storageApi = wx.getStorageSync("apiUrl")
if (storageApi) {
  apiUrl = storageApi
}
//封装http方法给api.js直接使用
const http = (params) => {
  //返回promise 对象
  return new Promise((resolve, reject) => {
    wx.request({
      url: apiUrl + params.url,
      data: params.data,
      header: params.header || {
        "Content-Type": "application/x-www-form-urlencoded",
        "token": wx.getStorageSync("token")
      },
      method: params.method || 'POST',
      dataType: params.dataType,
      responseType: params.responseType,
      success: function(res) {
        //1. 根据小程序打开后第一个接口请求判断小程序访问环境        
        if (!wx.getStorageSync("apiUrl") && params.url == "/goods/img" && res.statusCode == 200 && res.data) {
          //前端根据后端返回的reffer内容进行截取,获取判断环境的变量(后端在第一个约定的接口中将请求头中的reffer返回)
          const version = res.data.reffer && res.data.reffer.split('/')[4]
          if (!version || version == 0 || version == "devtools") {
            //非正式环境(开发者环境,开发版、体验版),保存测试服务器地址到缓存,并设置为测试服务器,然后重调本接口
            wx.setStorageSync("apiUrl", apiUrlDev)
            apiUrl = apiUrlDev
            //返回-1状态码给调用该接口的方法进行回调
            resolve({
              retCode: "-1"
            })
          }
        } else {
          //2. 非正式环境,所有接口访问都在控制台输出访问接口及响应数据,以便于调试
          if (storageApi) console.log("api", params.url, '::', res)

          //3. 接口响应数据正常处理逻辑,仅在生产环境接口访问出错时,控制台输出接口及响应数据
          if (res.statusCode == 200) {
            if (res.data.retCode != "000000" && !storageApi) console.log("api", params.url, '::', res)
            resolve(res.data)
          } else {
            wx.showToast({
              title: "系统繁忙,请稍后再试~",
              icon: "none"
            })
            if (!storageApi) console.log("api", params.url, '::', res)
          }
        }
      },
      fail: function(e) {
        wx.showToast({
          title: "系统繁忙,请稍后再试~",
          icon: "none"
        })
        reject(e)
      }
    })
  })
}
module.exports = {
  http: http
}

如上,主要做了两个比较大的改动:

1.在服务器地址设置的逻辑中,默认为生产环境服务器地址,如果缓存中有apiUrl,则使用缓存中的地址:

1 var apiUrl = "https://xxx.xxxx.cn"; //生产环境服务器地址
2 var apiUrlDev = "http://xxx.xxxx.cn";//测试环境服务器地址
3 var storageApi = wx.getStorageSync("apiUrl")
4 //缓存中有服务器地址,则使用缓存中的服务器地址
5 if (storageApi) {  
6   apiUrl = storageApi
7 }

2.在响应数据处理的逻辑中,如果缓存中没有保存服务器地址(apiUrl)且是指定的接口(小程序第一个必须访问且与后端约定返回请求头中的reffer给前端使用),则获取响应数据中的reffer,并截取reffer中的version字段:

1         if (!wx.getStorageSync("apiUrl") && params.url == "/goods/img" && res.statusCode == 200 && res.data) {
2           //缓存中无apiUrl且是第一个必须访问的接口,则获取reffer(与后端约定返回这个值)
3           var version = res.data.reffer && res.data.reffer.split('/')[4]
4           ...

3.对reffer中的version进行判断,如果是0或“devtools”,则将测试服务器地址保存到缓存中,并返回-1给调用该接口的方法进行回调:

1           if (!version || version == 0 || version == "devtools") {
2             //非正式环境(开发者环境,开发版、体验版),保存测试服务器地址到缓存,并设置为测试服务器,然后重调本接口
3             wx.setStorageSync("apiUrl", apiUrlDev)
4             apiUrl = apiUrlDev
5             //返回-1状态码给调用该接口的方法进行回调
6             resolve({
7               retCode: "-1"
8             })
9           }

4. 页面业务逻辑代码部分也要做相应调整:

1         if (data.retCode == "-1"){
2           self.loadGood(goods_id)
3           return;
4         }

经过上边的改造,正式版小程序第一个接口访问中不符合  version == 0 || version == "devtools" 条件而不再执行条件判断后续代码,对当前接口数据处理及后续其他接口访问都无影响。之所以加了 !version 这个条件,是因为开发阶段,新增的这个字段还未同步到正式环境,所以做了这个兼容,即没有这个字段则直接访问测试环服务器。

对于测试环境,则在启动小程序的时候,访问第一个接口 "/goods/img" ,服务器返回reffer值可以判断出非正式环境,则将测试服务器地址保存到缓存中,并回调当前接口 http(params); ,这样就会重新调用当前接口,后续其他接口访问则直接访问测试服务器。

至此,代码改造完成,剩下的就是在不同环境中进行验证了。

注意事项:

1. 本方法只能算曲线救国,如果是非正式环境,则第一个请求接口会请求两次,第一次访问正式服务器,第二次访问测试服务器,其他就没多大影响了。可以直接让后端单独写一个接口来判断小程序运行环境,这样就不需要改动原有接口了。

2. 无论是采用第一个接口,还是单独写接口,都是需要先访问一次正式环境的,这个没办法,因为我们目前采用的是小程序网络请求的请求头来判断运行环境的。

3. 虽然不尽完美,但在目前的情况下,貌似也只能这样处理了,至少以后不用每次发布版本的时候再手动改服务器访问地址了。


后续:

2018.12.29  

发现小程序提审的时候,腾讯是通过体验版进行审核验证的,所以如果要使用本文中对生产、非生产(开发、体验)环境进行区分的方法,测试服务器也需要支持https访问,并绑定到小程序管理后台的request域名中去。不然应该是审核不通过的了。

还有另外一种方法,就是复用代码包再创建一个测试用的小程序(无需申请小程序,仍使用原来的appid)进行开发调试,带开发环境验证没问题,再将代码合并到正式小程序代码中,这样测试小程序链接测试服务器,正式小程序项目链接生产环境,这样开发调试就不会影响到正式小程序的提审发布了。

 

posted on 2018-11-22 16:08  逍遥云天  阅读(14754)  评论(0编辑  收藏  举报

导航