前言:

之前用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,则使用缓存中的地址:

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

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

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

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

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

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

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

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

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

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

注意事项:

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

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

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


后续:

2018.12.29  

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

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

微信小程序开发——前端如何区分小程序运行环境的更多相关文章

  1. 微信小程序开发教程 #043 - 在小程序开发中使用 npm

    本文介绍了如何在微信小程序开发中使用 npm 中包的功能,大大提高微信小程序的开发效率,同时也是微信小程序系列教程的视频版更新. 微信小程序在发布之初没有对 npm 的支持功能,这也是目前很多前端开发 ...

  2. 微信小程序--关于加快小程序开发的几个小建议

    加快小程序开发的几个小建议 1.使用 app.json创建页面 ​ 按照我们平常的开发习惯,创建一个新的页面,一般都会先创建文件夹,再创建对应page的形式,创建完成后,app.json中会自动注册该 ...

  3. Win32 程序开发:创建一个应用程序窗口

    一.创建一个应用程序窗口 代码如下: // 头文件 #include <windows.h> // 全局变量 WCHAR g_lpszClassName[] = L"CLASSN ...

  4. vsCode怎么为一个前端项目配置ts的运行环境

    vsCode为一个前端项目配置ts的运行环境,ts文件保存的时候自动编译成js文件: 假设此前端项目名称为Web:文件结构如图 1. 在根目录中新建一个“.vscode”文件夹,里面建一个“tasks ...

  5. 微信小程序开发公测,小程序账号申请办法攻略

    11月3号晚上 10 点,微信公众平台发布公告,宣布微信小程序正式开放公测.此次小程序公测允许开发者将产品提交至微信公众平台审核,但是暂时不支持发布,也就是说普通消费者若想体验小程序,还需要等待一段时 ...

  6. 微信小程序开发——打开另一个小程序

    微信小程序打开另一个小程序,有两种方法:1.超链接:2.点击按钮. 全局配置: 跳转到其他小程序,需要在当前小程序全局配置中配置需要跳转的小程序列表,代码如下: App.json { ... &quo ...

  7. 跟我一起,利用bitcms内容管理系统从0到1学习小程序开发:一、IIS下SSL环境搭建

    缘起 1.从事互联网十来年了,一直想把自己的从事开发过程遇到的问题给写出来,分享给大家.可是可是这只是个种想法,想想之后就放下了,写出来的类文章是少之又少.古人说无志之人常立志,有志之人立长志.今天, ...

  8. docker 和 vagrant 作为程序发布 和 开发的独立而统一的运行环境

    docker 和 vagrant 作为程序发布 和 开发的运行环境,可以提供打包程序,并使得程序运行在一个独立的虚拟环境中,避免程序发布到客户机之后,环境不一致导致的诸多问题.     refer: ...

  9. 《Symfony 5全面开发》教程02、安装运行环境并初始化Symfony项目

    Symfony是PHP框架,在学习Symfony之前,我们需要安装PHP运行环境.如果你是MacOS系统,可以使用Homebrew来安装PHP运行环境. Homebrew官网 https://brew ...

随机推荐

  1. ADOQuery.Parameters: Property Parameters does not exist

    Exception class EReadError with message 'Property Parameters does not exist'. Exception class EReadE ...

  2. delphi 连接DBF

    delphi 连接DBF 使用 DBF 文件时,文件夹表示数据库,单个 DBF 文件表示表 1.BDE,已淘汰 table1.Databasename:=dbdir;//设置库路径table1.Tab ...

  3. putty登录显示IP

    登陆服务器 cd vi .bashrc 在尾部加入如下代码 if [ "$SSH_CONNECTION" != '' -a "$TERM" != 'linux' ...

  4. 17_react脚手架应用分析

    |-- index.html // 启动页(主页) |-- build //构建目录,遵循发布系统规范 | |-- index.html //静态页面 | |-- static //资源文件发布到cd ...

  5. Django权限auth模块详解

    转自:http://www.cnblogs.com/Finley/p/5575305.html 1,auth模块是Django提供的标准权限管理系统,可以提供用户身份认证,用户组和权限管理 2,aut ...

  6. SSL 链接安全协议的enum

    摘自:https://blog.csdn.net/lan_liang/article/details/70948221 在进行HTTPS连接时,需要指定SecurityProtocol.对于.NET ...

  7. python catch socket timeout

    python catch socket timeout import socket try: # do something. except socket.timeout as e: # socket ...

  8. TCP连接异常断开检测(转)

    TCP是一种面向连接的协议,连接的建立和断开需要通过收发相应的分节来实现.某些时候,由于网络的故障或是一方主机的突然崩溃而另一方无法检测到,以致始终保持着不存在的连接.下面介绍一种方法来检测这种异常断 ...

  9. 多线程(Java)

    Thread 类 和 Runnable 接口 在Java中实现多线程有两种方法 继承 Thread 类 优点:通过覆盖Thread 类的方法,可以改变线程的行为. 实现 Runnable 接口 优点: ...

  10. 1.Tomcat配置.md

    1.启动 解压缩安装包后,点击startup.bat,保持控制台窗口开启 浏览器中输入http://localhost:8080 后看到启动界面则表示启动成功 点击shutdown.bat则关闭Tom ...