前言
我们通过调用公众号接口是可以直接跳转小程序的,但是有时我们的需求并非如此,比如,我们需要跳转到某个H5页面,再从H5页面中跳转到指定的小程序。接下来请看下面的代码。
一、参考文档
JS-SDK说明文档,跳转小程序的标签,获取公众号token,签名算法。
我就是根据上面这四篇官方文档来实现该功能的。
二、后端springboot代码
1.导入依赖
<!--http工具包-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
<scope>compile</scope>
</dependency>
<!--json解析-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
2.H5页面跳转小程序所需的各种数据
代码如下(示例):
@Controller
public class TestController {
@Autowired
private RestTemplate restTemplate;
/**
* @作者 yangs
* @日期 2022/5/17
* @描述 H5跳转小程序需要的数据
*/
@RequestMapping("/getData")
public HashMap<String, String> getData() {
String jsapi_ticket = getJsApiTicket();
String nonceStr = UUID.randomUUID().toString();
String timestamp = "1647935255";
//这里填写你的H5页面的url
String url = "http://351s3728.51vip.biz/toWx";
String[] arr = new String[]{jsapi_ticket, nonceStr, timestamp, url};
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
if (i != 0) {
content.append("&");
}
String s = arr[i];
if (s.equals(nonceStr)) {
content.append("noncestr=");
} else if (s.equals(jsapi_ticket)) {
content.append("jsapi_ticket=");
} else if (s.equals(timestamp)) {
content.append("timestamp=");
} else if (s.equals(url)) {
content.append("url=");
}
content.append(arr[i]);
}
System.out.println("准备签名的字符串-->" + content);
// 签名规则 请参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62
String signature = hexSHA1(content.toString());
HashMap<String, String> map = new HashMap<>();
//支付签名时间戳
map.put("timestamp", timestamp);
//生成签名的随机串
map.put("nonceStr", nonceStr);
//签名
map.put("signature", signature);
return map;
}
/**
* @作者 yangs
* @日期 2022/5/17
* @描述 获取小程序jsapi_ticket
*/
public String getJsApiTicket() {
HashMap<String, String> map = new HashMap<>();
// 公众号的accessToken直接从redis读取了
String token = AppRedisUtil.get("accessToken");
map.put("access_token", token);
map.put("type", "jsapi");
String s = sendGet("https://api.weixin.qq.com/cgi-bin/ticket/getticket", map);
return s;
}
/**
* @描述 每隔60分钟获取access_token并存入redis
* @参考文档 https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
* @作者 yangs
* @日期 2022/5/17
*/
@Scheduled(initialDelay = 1000, fixedRate = 1000 * 60 * 60)
public void queryTokenToRedis() {
String path = "?grant_type={grant_type}&appid={appid}&secret={secret}";
Map<String, String> params = new HashMap<>(3);
params.put("grant_type", "client_credential");
params.put("appid", "你公众号的appid");
params.put("secret", "你公众号的secret");
ResponseEntity<String> forObject = restTemplate.getForEntity("https://api.weixin.qq.com/cgi-bin/token" + path, String.class, params);
JSONObject jsonObject = JSONObject.parseObject(forObject.getBody());
String accessToken = jsonObject.getString("access_token");
if (accessToken != null) {
//有效期设置65分钟
AppRedisUtil.set("accessToken", 65 * 60, accessToken);
} else {
AppRedisUtil.set("accessToken", forObject.getBody());
}
}
/**
* @作者 yangs
* @日期 2022/3/22
* @描述 sha1算法签名
*/
private String hexSHA1(String value) {
try {
MessageDigest md = MessageDigest.getInstance("sha1");
md.update(value.getBytes("utf-8"));
byte[] digest = md.digest();
return String.valueOf(Hex.encodeHex(digest));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
/**
* 下面是封装的一些发送http请求的公共方法,当然你也可以使用你自己的
*/
private String sendGet(String reqUrl, Map<String, String> params) {
InputStream inputStream = null;
HttpGet request = new HttpGet();
try {
String url = buildUrl(reqUrl, params);
HttpClient client = new DefaultHttpClient();
request.setHeader("Accept-Encoding", "gzip");
request.setURI(new URI(url));
HttpResponse response = client.execute(request);
inputStream = response.getEntity().getContent();
return getJsonStringFromGZIP(inputStream);
} catch (URISyntaxException | IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null)
inputStream.close();
request.clone();
} catch (IOException | CloneNotSupportedException e) {
e.printStackTrace();
}
}
return null;
}
private String getJsonStringFromGZIP(InputStream is) {
String jsonString = null;
try {
BufferedInputStream bis = new BufferedInputStream(is);
bis.mark(2);
// 取前两个字节
byte[] header = new byte[2];
int result = bis.read(header);
// reset输入流到开始位置
bis.reset();
// 判断是否是GZIP格式
int headerData = getShort(header);
// Gzip 流 的前两个字节是 0x1f8b
if (result != -1 && headerData == 0x1f8b) {
is = new GZIPInputStream(bis);
} else {
is = bis;
}
InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8);
char[] data = new char[100];
int readSize;
StringBuilder sb = new StringBuilder();
while ((readSize = reader.read(data)) > 0) {
sb.append(data, 0, readSize);
}
jsonString = sb.toString();
bis.close();
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return jsonString;
}
private static int getShort(byte[] data) {
return (data[0] << 8) | data[1] & 0xFF;
}
/**
* @描述 构建get方式的url
* @作者 yangs
* @日期 2021/8/14
*/
private String buildUrl(String reqUrl, Map<String, String> params) {
StringBuilder query = new StringBuilder();
Set<String> set = params.keySet();
for (String key : set) {
query.append(String.format("%s=%s&", key, params.get(key)));
}
return reqUrl + "?" + query.toString();
}
}
三、前端HTML代码
<html>
<head>
<title>跳转小程序测试</title>
<%--需要导入微信提供的这个js文件--%>
<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script src="${pageContext.request.contextPath}/js/jQ.js"></script>
</head>
<body>
跳转小程序
<wx-open-launch-weapp
id="launch-btn"
username="gh_a26325160123"
path="/pages/home/homeHome/homeHome">
<script type="text/wxtag-template">
<style>.btn {
padding: 12px
}</style>
<button class="btn" id="btn">打开小程序</button>
</script>
</wx-open-launch-weapp>
<script>
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印
appId: 'wx56327e8a9e43f215', // 必填,公众号的唯一标识
timestamp: ${timestamp}, // 必填,生成签名的时间戳,后端读取
nonceStr: '${nonceStr}', // 必填,生成签名的随机串,后端读取
signature: '${signature}',// 必填,签名,后端读取
jsApiList: ['updateAppMessageShareData'], // 必填,需要使用的JS接口列表
openTagList: ['wx-open-launch-weapp'] // 可选,需要使用的开放标签列表,例如['wx-open-launch-app']
});
wx.ready(function () {
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中
console.log("验证成功!");
var btn = document.getElementById('launch-btn');
btn.addEventListener('launch', function (e) {
console.log('success');
});
btn.addEventListener('error', function (e) {
console.log('fail', e.detail);
});
});
wx.error(function (res) {
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名
console.log("验证失败!");
});
</script>
</body>
</html>
总结
以上就是H5页面跳转小程序的全部代码。另外各位大佬,请允许我打个小小的广告,下面是我开发的关于做人做饭的食谱小程序,感兴趣的扫描下方二维码去看看吧。