这几天紧急开发一个拼团+砍价的H5微信小商城。技术用的是:前端Vue+Vant快速开发组件框架、后端java。
- 使用微信js-sdk流程图:
- 微信公众平台测试帐号申请地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
- 微信Js-sdk官方文档地址: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
这里记录一下微信分享自定义:
1. 申请微信测试号
测试账号拥有几乎所有的公众号接口,而个人只能申请订阅号,几乎没有接口可用,并且消息推送还会受到次数限制。如果只是做开发测试的话,那么测试帐号比较好用。
扫码登录即申请成功!登录后可以看到测试号的appID、appsecret
设置一下js安全接口域名,设置成自己的本机Ip即可。
扫码关注测试公众号,现在就可以使用微信的接口了。
2. 获取Access token
可以看到文档就是让我们发起一个get请求, 携带三个参数:
这里指的就是测试号的appID、appsecret。
/***
* 获取微信的token
* @return
*/
public static WxToken getAccessToken(String appId,String secret){
logger.info("getAccessToken()---begin");
WxToken wxToken=new WxToken();
Map<String, String> formDataPartMap = new HashMap<String, String>();
formDataPartMap.put("grant_type", Constans.WX_GRANT_TYPE);
formDataPartMap.put("appId",appId);
formDataPartMap.put("secret",secret);
Result syncRequest;
try {
logger.info("getAccessToken()---开始调用微信基本接口获取accessToken");
syncRequest = OkHttpUtils.getData(Constans.WX_ACCESS_TOKEN_URL, formDataPartMap);
if (ObjectUtils.isNotEmpty(syncRequest) && syncRequest.getCode() == 200) {
JSONObject jsonObject = JSON.parseObject(syncRequest.getMsg());
logger.info("getAccessToken()---access_token:"+jsonObject);
wxToken.setAccessToken(jsonObject.getString("access_token"));
wxToken.setExpiresIn(jsonObject.getString("expires_in"));
wxToken.setWxAppId(appId);
logger.info("getAccessToken()---access_token:"+jsonObject.getString("access_token"));
logger.info("getAccessToken()---expires_in:"+jsonObject.getString("expires_in"));
logger.info("getAccessToken()---获取accessToken成功!!!");
}
} catch (IOException e) {
e.printStackTrace();
logger.info("getAccessToken()---获取AccessToken发生异常");
}
logger.info("getAccessToken()---end");
return wxToken;
}
3. 获取jsapi_ticket
根据获取到的access_token来获取jsapi_ticket,也是get请求!
/***
* 获取微信的ticket
* @param accessToken
* @return
*/
public static String getTicket(String accessToken){
logger.info("getTicket()---begin");
String ticket = null ;
Map<String, String> formDataPartMap = new HashMap<String, String>();
formDataPartMap.put("access_token", accessToken);
formDataPartMap.put("type", "jsapi");
Result syncRequest;
try {
logger.info("getTicket()---开始调用微信基本接口获取ticket");
syncRequest = OkHttpUtils.getData(Constans.WX_TICKET_URL, formDataPartMap);
if (ObjectUtils.isNotEmpty(syncRequest) && syncRequest.getCode() == 200) {
JSONObject jsonObject = JSON.parseObject(syncRequest.getMsg());
jsonObject = JSON.parseObject(syncRequest.getMsg());
ticket = jsonObject.getString("ticket");
logger.info("getTicket()---ticket:"+jsonObject.getString("ticket"));
logger.info("getTicket()---获取ticket成功!!!");
}
} catch (IOException e) {
e.printStackTrace();
}
logger.info("getTicket()---end");
return ticket;
}
正常情况下,access_token 和 jsapi_ticket的有效期为7200秒,由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
按照微信的推荐方案这两个方法必须要在服务端调用,并使用缓存或数据库存储。 解决方案: 在服务端启动后执行定时器,每隔7000秒定时刷新token及ticket。存储在数据库或缓存中间件例如: redis中, 并向客户端提供一个主动刷新token及ticket的方法
我这里为了减少项目的依赖性,存储在数据库。 如果项目中使用了redis,更加建议使用redis存储。代码如下:
@Component
public class ApplicationRunnerImpl extends BaseService implements ApplicationRunner {
private static Logger logger = LoggerFactory.getLogger(ApplicationRunnerImpl.class);
@Override
public void run(ApplicationArguments args) throws Exception {
//查询wx配置
WxServiceConfig wxServiceConfig = wxServiceConfigRepository.findOne(Constans.WX_SERVICE_CONFIG_ID);
logger.info("通过实现ApplicationRunner接口,在springboot项目启动后执行方法");
logger.info("项目启动成功两秒后,执行Timer,后续每次执行间隔为7000秒");
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
logger.info("执行Timer开始");
logger.info("获取微信的accessToken");
// 获取微信的accessToken
WxToken wxToken = WxTools.getAccessToken(wxServiceConfig.getWxAppId(),wxServiceConfig.getWxSecret());
logger.info("获取微信的ticket");
if(ObjectUtils.isNotEmpty(wxToken.getAccessToken())){
String ticket = WxTools.getTicket(wxToken.getAccessToken());
wxToken.setWxTicket(ticket);
wxToken.setCreateTime(new Date());
WxToken save = wxTokenRepository.save(wxToken);
if(ObjectUtils.isEmpty(save)) logger.info("更新或存储token信息失败");
}else{
logger.info("获取失败微信token失败,可能是没有绑定白名单或jssdk信息");
}
logger.info("执行Timer结束");
}
}, 2000, 7000 * 1000);
}
}
4. 生成签名
签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
/***
* 获取签名方法
* @param timestamp
* @param nonceStr
* @param url
* @param ticket
* @return
* @throws Exception
*/
public static String getSign(String timestamp, String nonceStr,String url,String ticket) throws Exception {
logger.info("getSign()---begin");
StringBuffer sb = new StringBuffer();
sb.append("jsapi_ticket=").append(ticket).append("&");
sb.append("noncestr=").append(nonceStr).append("&");
sb.append("timestamp=").append(timestamp).append("&");
sb.append("url=").append(url);
logger.info("getSign()---param:"+sb.toString());
String sha1 = DigestUtils.sha1Hex(sb.toString().getBytes("UTF-8"));
logger.info("getSign()---sha1: "+sha1);
logger.info("getSign()---end");
return sha1;
}
5. 引入js文件
Vue项目引入方式:
npm install weixin-js-sdk //下载sdk
import wx from 'weixin-js-sdk' #需要使用的页面引入
使用直接 wx.xxx
js引入方式:在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.6.0.js
当资源不可访问时,可改访问:http://res2.wx.qq.com/open/js/jweixin-1.6.0.js (支持https)
6. config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用)
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: wxConfig.appId, // 必填,公众号的唯一标识
timestamp: wxConfig.timestamp, // 必填,生成签名的时间戳
nonceStr: wxConfig.nonceStr, // 必填,生成签名的随机串
signature: wxConfig.signature, // 必填,签名 服务端通过token和ticket生成的签名
jsApiList: [
"updateAppMessageShareData", //<strong>自定义“分享给朋友”及“分享到QQ”按钮的分享内容(1.4.0)</strong>
"updateTimelineShareData", //<strong>自定义“分享给朋友圈”及“分享到QQ空间”按钮的分享内容(1.4.0)</strong>
"openLocation" //打开地图
], // 必填,需要使用的JS接口列表 如果没填 接口会使用不了
});
7. 通过ready接口处理成功验证
config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
wx.ready(()=>{ //需在用户可能点击分享按钮前就先调用
wx.updateAppMessageShareData({ //自定义“分享给朋友”及“分享到QQ”按钮的分享内容(1.4.0)
title: this.wxUserInfo.nickname+': 车位万元优惠重磅来袭!车位砍价活动火热进行中...', // 分享标题
desc: '车位万元优惠重磅来袭!', // 分享描述
link: 'http://192.168.0.112:8080/car?bargain_openid='+this.wxUserInfo.openid, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: 'http://47.119.164.247:9000/zapi/020747bb9e97b7fc45c96741e104025.jpg', // 分享图标
success: function () {
// 设置成功
}
});
wx.updateTimelineShareData({ //自定义“分享给朋友圈”及“分享到QQ动态”按钮的分享内容(1.4.0)
title: this.wxUserInfo.nickname+': 车位万元优惠重磅来袭!车位砍价活动火热进行中...', // 分享标题
desc: '车位万元优惠重磅来袭!', // 分享描述
link: 'http://192.168.0.112:8080/car?bargain_openid='+this.wxUserInfo.openid, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: 'http://47.119.164.247:9000/zapi/020747bb9e97b7fc45c96741e104025.jpg', // 分享图标
success: function () {
// 设置成功
}
});
});
8. 使用接口
所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数:
- success:接口调用成功时执行的回调函数。
- fail:接口调用失败时执行的回调函数。
- complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。
- cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。
- trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。
例如:
wx.openLocation({
latitude: 50.199767, // 纬度,浮点数,范围为90 ~ -90
longitude: 135.971021, // 经度,浮点数,范围为180 ~ -180。
name: '万达广场', // 位置名
address: '街60-160号附近', // 地址详情说明
scale: 15, // 地图缩放级别,整型值,范围从1~28。默认为最大
infoUrl: '', // 在查看位置界面底部显示的超链接,可点击跳转
success: function () {
// 设置成功
}
});
附上我的http请求方法:
/**
* get请求,同步方式,获取网络数据,
*
* @param url
* @return
*/
public static Result getData(String url) throws IOException {
// 1 构造Request
Request.Builder builder = new Request.Builder().get().url(url);
OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS).build();
// 2 将Request封装为Call
Call call = okHttpClient.newCall(builder.build());
// 3 执行Call,得到response
Response response = call.execute();
if (response.isSuccessful()) {
return new Result(Result.CODE_SUCCESS, response.body().string());
} else {
return new Result(Result.CODE_ERROR, response.message());
}
}
public static Result getData(String url, Map<String, String> params) throws IOException {
StringBuilder urlPath = new StringBuilder(url);
if (ObjectUtils.isNotEmpty(params)) {
urlPath.append("?");
for (Map.Entry<String, String> param : params.entrySet()) {
urlPath.append(param.getKey() + "=" + param.getValue() + "&");
}
urlPath.deleteCharAt(urlPath.length() - 1);
}
System.out.println(urlPath.toString());
return getData(urlPath.toString());
}
OK完工。以上就是H5实现微信分享自定义&地图的全部内容,如果还有其他不懂的可以添加右下角微信,欢迎骚扰!
大家也可以点击我的gitee来获取我的其他项目源码。 拜拜!