重要声明:本文章仅仅代表了作者个人对此观点的理解和表述。读者请查阅时持自己的意见进行讨论。
目录
本系列博文还包含了下面的博客:
- 【微信公众号开发】一、运作及配置流程简介
- 【微信公众号开发】二、解析微信请求及响应消息
- 【微信公众号开发】三、解析微信事件XML数据消息及响应
- 【微信公众号开发】四、公众号按钮设置及自己的微信按钮编辑器
- 【微信公众号开发】五、微信网页授权获取用户openId
- 【微信公众号开发】六、微信JS的使用(本文)
- 【微信公众号开发】七、微信JS需要注意的坑
- 【微信公众号开发】八、微信JS发起支付
一、微信JS介绍和注意事项
1、微信js介绍
但凡是做微信公众号开发,基本上逃不开对微信前端页面的开发,涉及了页面,那也必然就会涉及到使用微信js来完成一些功能了。微信js为我们提供了哪些方法?又能做些什么事情呢?下面介绍几种使用场景十分频繁的功能。
分享,很多时候用户都有分享自己认为不错的事物的习惯,如果我们不调用微信js去包装分享内容,那么用户分享出去的网页就默认显示的网页标题和网址,并附加一张页内第一张img图片。通过微信js的分享方法,我们可以设定自己想要的标题和图标,还有自定义的文案描述内容。这样就可以以更友好的方式将页面分享出去。
上传图片,在页面里常常遇到需要选择图片进行上传的场景,虽然在微信的浏览器里可以使用input type=file
来进行h5的方式选择文件,但其用户体验不够好。微信提供了一个非常便捷的且迎合微信操作习惯的选择图片的方式,这样就可以让用户选择图片的功能在微信中得到统一,这无疑会大幅提升用户体验。
菜单按钮(隐藏/显示),细心的朋友可能会发现,一个普通的页面在微信浏览器里打开后,点击右上角的菜单按钮,就可以看到许多的功能项提供给用户选择,加入我们不希望用户能对页面进行某些操作时怎么办?放心,微信也提供了相关的解决方案。我们可以调用微信提供的隐藏按钮方法,将一些按钮(例如:复制链接、在浏览器打开等)进行隐藏,这样用户就操作不到相关敏感功能了。当你要再次进行显示某按钮时,你又可以调用显示按钮方法进行显示。
关闭页面,操作完成,提示用户可以关闭了。你以为你一句 window.close() 就能搞定?那你就 图痒图森破 了。在微信里要关闭页面回到公众号界面,你需要调用微信提供的关闭方法来关闭。
支付,开发商店类的微信公众号的朋友一定知道,在页内直接发起微信支付让用户付款这个行为是多么的有必要。微信提供了支付方法供我们调用,可以方便的直接将用户的钱钱收入囊中。
其他还有许多的微信js方法提供、不过最常用的还是上面几个。
2、注意事项
你的页面要能使用微信js,先导条件是将你页面的域名设置到微信公众号后台中。在微信后台点击【公众号设置】【功能设置】即可找到微信js安全域名设置项。如果没设置完成,那么你将无法在你的页面中使用微信js。
二、开始接入
就像你在页面里引入JQuery
那样简单,你就可以用相同的方法引入微信提供的js文件。微信js文件链接如下:
这是截止本文编写时间最新的微信js文件地址,它支持https的访问。我使用图片展示它,是因为可能现在这已经不是最新的了。因为我希望你可以到“微信官方文档”这里去复制微信给出的最新地址。
有了地址,直接使用script
标签进行引入:
<script src="https://res.wx.qq.com/open/js/jweixin-x.x.x.js"></script>
现在,你就拥有了微信提供的各种方法。但是你只是拥有了它的身体,你还没有拥有它的心。要想正常的调用里面的各个方法,你需要先要配置微信js的使用授权。
三、配置js授权
当你引入了微信js后,它会在 window
下为你挂载 wx
这个对象。所有的方法你就要通过它进行调用。配置微信js授权也是要用它:
wx.config(option);
使用 config
方法就可以进行配置,但是其option参数是必选的一个对象。而这个对象的内容是:
{
debug: true, // 选填,开启调试模式,调用的所有api的返回值会在客户端alert出来,
// 若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,
// 仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: '', // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '', // 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
}
这些东西看起来复杂度可不小,为了安全起见,这些参数官方建议在后端生成,然后下发给前端使用。为什么要后台生成?
观察参数字段,其中signature
是签名,这个签名来头可不小,它是一个sha1的数据签名,而签名数据源就需要下面的东西:
- noncestr: 随机字符串
- jsapi_ticket: js票据
- timestamp: 时间戳
- url: 当前页面url
其中jsapi_ticket
是微信提供的API接口拿到的,所以此数据最优雅就是在后端获取,既然这个在后端获取,那后端干脆就顺便把上边整个option参数体全部组建好,直接返回给前端,那么前端config
的事情就变得有趣起来。
四、编写后台接口
在我们之前的两篇文章【微信公众号开发】一、运作及配置流程简介 以及【微信公众号开发】二、解析微信请求及响应消息 中建立的WeChatController
类中新增一个方法,用于方便获取微信js配置:
@Controller
@RequestMapping("/wechat")
public class WeChatController {
/* 之前的代码。 */
/* 为节省篇幅,此处省略。 */
@RequestMapping("/getWxJsCfg")
@RequestBody
public String getWxjsCfg(HttpServletRequest request) throws Exception {
return null;
}
}
现在我们需要完善getWxjsCfg
来生成我们微信js需要的参数。
1、获取 jsapi_ticket
使用微信API即可方便的获取到jsapi_ticket
,接口地址为:
<!-- 接口地址 -->
https://api.weixin.qq.com/cgi-bin/ticket/getticket
<!-- 参数列表 -->
access_token: ACCESS_TOKEN
type: 写死值:"jsapi"
使用 GET 方法请求这个地址即可获取到结果, 不过其中 ACCESS_TOKEN
需要注意,这也需要调用微信API获取,关于 ACCESS_TOKEN
的获取,请参考:《【微信公众号开发】四、公众号按钮设置及自己的微信按钮编辑器》 文中第一节的内容。接下来继续完善接口代码:
@RequestMapping("/getWxJsCfg")
@RequestBody
public String getWxjsCfg(HttpServletRequest request) throws Exception {
// 先获取必须的参数。
// 通常传递当前页面的url。
String url = request.getParameter("url");
// 获取 jsapi_ticket
String ticket = Util.getCache("ticket"); // 先尝试从缓存获取。
if (Util.isEmpty(ticket)) {
// 缓存里没有获取到数据。则请求微信api获取。
// 构建参数并请求
Map<String, String> param = new HashMap<>();
param.put("access_token", "ACCESSTOKEN的值");
param.put("type", "jsapi");
String result = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket", param);
// 解析结果
JSONObject resultJs = JSON.parseObject(result);
if (0 != resultJs.getIntValue("errcode") || !"ok".equalsIgnoreCase(resultJs.getString("errmsg")))
// 出错了。直接抛出,交给统一错误处理。
throw new Exception(resultJs.getString("errmsg"));
}
// 拿到 js票据 和 过期时间。
ticket = resultJs.getString("ticket");
int expires_in = resultJs.getIntValue("expires_in"); // 这个时间单位是 秒
// 保存到缓存。
Util.setCache("ticket", ticket, expires_in);
}
return null;
}
代码里首先获取了基本参数。url
为了获取方便,前端将其url传入进来,注意如果你的页面是单页面的并且你的页面上的路由设定的是hash路径格式,那么你只需要将整个url中#
号前面部分的内容传递给后端进行就可以了。
然后是从缓存里获取原来是否保存过js票据,由于此票据可以使用2小时,所以,这里增加一个判断,这样减少api请求次数,效率也更高。当然了,第一次是拿不到缓存数据的,这时候就通过微信接口去获取票据,获取成功后将其保存到缓存,并且设定过期时间,过期后将不能从缓存中获取到。代码中出现了2个工具方法帮我们完成了缓存的设定和获取,这两个方法如下:
public class Util {
/* 为节省篇幅,其他代码已省略。 */
// 从缓存中获取某key对应的值。
public static String getCache(String key) {
// 方法内没有实现,开发者应根据自己系统决定实现方式。
return null;
}
// 将某key对应的值设置到缓存,并提供过期时间,在过期后将获取不到该值。
public static void setCache(String key, String value, int expirein) {
// 方法内没有实现,开发者应根据自己系统决定实现方式。
}
}
这两个方法的具体实现,需要各位根据自己系统的缓存解决方案去处理,可能你使用了redis
作为缓存,可能是直接使用内存缓存,也可能是数据库缓存。因此这两个方法需要自己去实现。
现在我们获取到了jsapi_ticket
票据,可以进行数据签名了。
2、数据签名
这是摘自微信文档官方的文案介绍:
签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
其中涉及了一些参数我们并未要求前端传入,我们可以自己生成,然后返回给前端使用就好了。所以在签名前我们先准备好需要的东西:
<!-- 准备数据 -->
jsapi_ticket: 上面已经获取到了
url: 上面已要求前端传入了
noncestr: 随机字符串 --> 随机生成
timestamp: 时间戳 --> 可使用当前时间
通过列表一展示出来,发现现在需要我们做的事情变得非常的简单了,继续完成接口代码, 为了节省篇幅,对于获取票据部分代码已隐藏:
@RequestMapping("/getWxJsCfg")
@RequestBody
public String getWxjsCfg(HttpServletRequest request) throws Exception {
// 先获取必须的参数。
// 通常传递当前页面的url。
String url = request.getParameter("url");
// 运行前端自己传递自己想要的哪些微信方法。多个使用逗号(英文逗号)隔开。
String jsApiList = request.getParameter("jsApiList");
// 获取 jsapi_ticket
String ticket = Util.getCache("ticket"); // 先尝试从缓存获取。
if (Util.isEmpty(ticket)) {
// 缓存里没有获取到数据。则请求微信api获取。
// 为了节省篇幅,此处代码已隐藏。
/* ... */
}
// 生成随机字符串
String noncestr = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16);
// 拿到当前时间戳, 这个只需要到秒的时间戳即可。
long timestamp = System.currentTimeMillis() / 1000;
// 然后进行【按照字段名进行字典排序】
String string1 = "jsapi_ticket=" + ticket +
"&noncestr=" + noncestr +
"×tamp=" + timestamp +
"&url=" + url;
// 最后进行sha1签名。
String signature = Util.sha1(string1);
// 然后返回给前端。
JSONObject cfgForWx = new JSONObject();
cfgForWx.put("appId", "自己微信公众号的appid");
cfgForWx.put("timestamp", timestamp);
cfgForWx.put("nonceStr", noncestr);
cfgForWx.put("signature", signature);
return cfgForWx.toJSONString();
}
上述代码中产生了随机字符串和时间戳。然后排序组建原数据,由于这个排序只需要根据字段名进行排序,所以直接按顺序写好即可,不用使用排序方法排序。最后进行签名,此处直接使用了Util中的快捷方法,要了解此方法,你可以到《【微信公众号开发】二、解析微信请求及响应消息》第四节进行浏览。
所有工作完成后,不要忘了将前端需要的必要参数返回给前端。返回数据格式可能各自系统都不一样,上方只是示列代码,自己的系统格式需要自己修改为相应格式。
五、前端调用接口获取配置
现在,后端已经为前端写好了获取配置的接口,那么前端要进行 wx.config(option)
前,调用接口,获取配置即可获取,假如你使用 Jquery 进行请求,则你可以参考下面的代码:
$.ajax({
url: "后台接口域名/wechat/getWxJsCfg",
type: 'POST',
data: {
url: window.location.href.split("#")[0]
},
dataType: 'json',
success: function (response) {
// 调起微信js的配置授权
wx.config({
appId: response.appId,
timestamp: response.timestamp,
nonceStr: response.nonceStr,
signature: response.signature,
// 需要使用的微信js方法
jsApiList: ["chooseImage","uploadImage","hideMenuItems", /*...等*/]
})
},
error: function (xmlHttpRequest, errorStr, exception) {
// 处理出错
}
});
进行了config
后,就可以顺利的使用微信提供的各个方法了。不过还要注意 wx.config(option)
方法是一个异步方法,也就意味着,这行代码执行完成后,下一行你就调起微信js的其他方法,很有可能依然会出现错误。你必须要等该方法config成功之后再调起相应的方法。好在微信提供了一个配置成功回调,你只需要提供一个配置成功后要执行的方法,就可以在方法中实现配置成功后要立刻执行的逻辑:
wx.ready(function(){
// 当config方法执行成功后,就会调用此方法,这里面的逻辑便可得到执行。
});
非常建议你在config
方法调用前就先调用 ready
方法将要立刻执行的逻辑写好。比如下面示列一个完整的【页面一打开就隐藏[复制链接按钮]的操作】:
<!-- 此代码示列了如何一打开界面就隐藏[复制链接按钮] -->
<html>
<head>
<title>页面一打开就隐藏[复制链接按钮]</title>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script>
<script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
</head>
<body>
<h1>页面一打开就隐藏[复制链接按钮]</h1>
<script>
$(function () {
// 首先定义好 config 配置成功执行的逻辑。
wx.ready(function () {
// 一旦 config 成功就隐藏按钮。
// 此处代码在 config 成功后立即执行,所以写在这里准没错。
wx.hideMenuItems({
menuList: ["menuItem:copyUrl"],
success: function (obj) {
console.log("隐藏成功。");
},
fail: function (obj) {
console.log("隐藏失败:", obj);
}
});
});
// 然后开始进行config。
// 开始前,先请求接口获取参数。
$.ajax({
url: "后台接口域名/wechat/getWxJsCfg",
type: 'POST',
data: {
url: window.location.href.split("#")[0]
},
dataType: 'json',
success: function (response) {
// 请求完成,调起微信js的配置授权
wx.config({
appId: response.appId,
timestamp: response.timestamp,
nonceStr: response.nonceStr,
signature: response.signature,
// 需要使用的微信js方法,此处演示只需要获取隐藏按钮api。
jsApiList: ["hideMenuItems"]
});
},
error: function (xmlHttpRequest, errorStr, exception) {
// 处理请求出错
}
});
});
</script>
</body>
</html>
六、总结
使用微信js的整体流程大致为:
获取js凭据
-->使用凭据和其他参数加签
-->前端进行config
-->成功后开始使用对应方法
。
本文涉及一个网络请求工具类HttpUtil
,如果你也是maven项目,你可以直接添加依赖来添加这个工具:
<dependency>
<groupId>cn.microanswer</groupId>
<artifactId>HttpUtil</artifactId>
<version>1.0.1</version>
</dependency>