最近公司开发微信内部的H5系统,需要调用微信的拍照和分享功能
根据微信提供的开发文档,一步一步的做信心满满结果。。。。
第一步:
由于是后台与前端的交互,先写后台代码,获取AccessToken和JsapiTicket
这两步很简单直接http get请求带参数,不细说了,贴出代码自己看
//其实生成Ticket主要是用到appId和secret,token主要是用来校验身份的不参与Ticket的生成
// 下边代码不全,仅供参考
public String getPubJsapiTicket() {
AccessToken accessToken = this.getPubAccessToken();
if (accessToken == null || StringUtils.isBlank(accessToken.getAccessToken())) {
LOGGER.error("获取access_token失败,无法继续获取jsapiTicket!");
return null;
}
String accessTokenStr = accessToken.getAccessToken();
String fetchPubAccessJsapiTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";//this.weChatProperties.getFetchPubAccessJsapiTicketUrl();
String url = fetchPubAccessJsapiTicketUrl + "?access_token=" + accessTokenStr + "&type=jsapi";
// {"errcode":0,"errmsg":"ok","ticket":"kgt8ON7yVITDhtdwci0qeaEfqG5oEkB5STVrkQEmlTaInv9p7JwVN-QM3Euqu0Ux60QKEAebjY0GQV_AYb3_4g","expires_in":7200}
Map map = this.rest.postForEntity(url, null, Map.class).getBody();
if(map.containsKey("ticket")) {
Object ticket = map.get("ticket");
return ticket.toString();
}else{
LOGGER.info("获取jsapi_ticket失败! url:{}", url);
return null;
}
}
//生成签名
String jsapiTicket = weChatTemplate.getJsapiTicket();
log.info("jsapiTicket={}", jsapiTicket);
//noncestr
String noncestr = UUID.randomUUID().toString();
log.info("noncestr={}", noncestr);
//timestamp
long timestamp = new Date().getTime() / 1000;
log.info("timestamp={}", timestamp);
//URl
StringBuffer url = request.getRequestURL();
log.info("url={}", url);
// 获取真实的url请求,区分当前请求是http还是https
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String header = headerNames.nextElement();
log.info("Header: {}, Value={}", header, request.getHeader(header));
}
}
String httpFlag = request.getHeader("X-Forwarded-Proto");
if (httpFlag != null && httpFlag.equals("https")) {
url.replace(0, 4, "https");
}
String queryString = request.getQueryString();
log.info("queryString={}", queryString);
if (StringUtils.isNotBlank(queryString)) {
url.append("?").append(request.getQueryString());
}
//签名字符串
StringBuffer signatureStrBuf = new StringBuffer("");
signatureStrBuf.append("jsapi_ticket=").append(jsapiTicket)
.append("&noncestr=").append(noncestr)
.append("×tamp=").append(timestamp + "").append("&url=").append(url);
log.debug("签名字符串 : " + signatureStrBuf.toString());
String signature = SHAUtils.shaSignature(signatureStrBuf.toString());
log.debug("签名 : " + signature);
上边这些都是些常规的操作,一步一步来,不会有太大问题,接下来写前端的代码
if (wxConfig.signature) {
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: wxConfig.appId, // 必填,公众号的唯一标识
timestamp: wxConfig.timestamp, // 必填,生成签名的时间戳
nonceStr: wxConfig.nonceStr, // 必填,生成签名的随机串
signature: wxConfig.signature,// 必填,签名,见附录1
jsApiList: ["onMenuShareTimeline", "onMenuShareAppMessage", "getLocation", 'chooseImage', 'getLocalImgData', 'previewImage', 'uploadImage', 'downloadImage'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
}
wx.ready(function(){
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,
// 所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。
// 对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
// wx.hideMenuItems({
// menuList: ["menuItem:share:qq","menuItem:share:weiboApp","menuItem:share:facebook","menuItem:share:QZone"] // 要隐藏的菜单项,只能隐藏“传播类”和“保护类”按钮
// });
var wechatShareObj = wechatShareData||{};
var title = wechatShareObj.title||"";
var link = encodeURI(wechatShareObj.link||"");
//朋友
var dialogueDesc = wechatShareObj.dialogueDesc||"";
var dialogueIcon = encodeURI(wechatShareObj.dialogueIcon||"");
//朋友圈
var circleDes = wechatShareObj.circleDes||"";
var circleIcon = encodeURI(wechatShareObj.circleIcon||"");
//获取“分享到朋友圈”按钮点击状态及自定义分享内容接口
wx.onMenuShareTimeline({
title: circleDes, // 分享标题
link: link, // 分享链接
imgUrl: circleIcon, // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
},
cancel: function () {
//取消分享到朋友圈回调
}
});
//获取“分享给朋友”按钮点击状态及自定义分享内容接口
wx.onMenuShareAppMessage({
title: title, // 分享标题
desc: dialogueDesc, // 分享描述
link: link, // 分享链接
imgUrl: dialogueIcon, // 分享图标
type: 'link', // 分享类型,music、video或link,不填默认为link
dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
success: function () {
// 用户确认分享后执行的回调函数
},
cancel: function () {
//取消分享回调
}
});
});
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
deleteImg:function(ev,n,localIds){
if (this.showImgs[n]) {
ev.target.value = '';
let iarr = this.showImgs.join(',').split(',');
iarr.splice(n, 1);
// this.images.splice(n, 1);
this.$set(this.$data, 'showImgs', iarr);
ev.preventDefault();
//移除descImgSummarys和uploadImgs对应元素
let descImgSummarys = this.descImgSummarys.join(',').split(',');
descImgSummarys.splice(n, 1);
this.$set(this.$data, 'descImgSummarys', descImgSummarys);
let uploadImgs = this.uploadImgs.join(',').split(',');
uploadImgs.splice(n, 1);
this.$set(this.$data, 'uploadImgs', uploadImgs);
}
},
chooseImg: function() {
var _this = this;
wx.chooseImage({
count: 4, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
_this.localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
_this.showImgs.pop();
for (var i = 0; i < _this.localIds.length; i++) {
_this.showImgs.push(_this.localIds[i]);
}
_this.showImgs.push("");
for (var i = 0; i < _this.localIds.length; i++) {
preOrderVm.uploadImg(_this.localIds, 0);
}
}
});
},
uploadImg:function (localIds,index) {
var uploadImgs = this.uploadImgs;
var descImgSummarys = this.descImgSummarys;
var _this = this;
try{
wx.uploadImage({
localId: localIds[index], // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function (data) {
var lastIndexOf = localIds[index].lastIndexOf("/");
descImgSummarys.push(localIds[index].substring(lastIndexOf+1));
uploadImgs.push(data.serverId);// 返回图片的服务器端ID
if(index < localIds.length) {
preOrderVm.uploadImg(_this.localIds,++index);
}
},
fail:function (res) {
console(res);
}
});
}catch(e){
console.error(e);
}
},
但是一直报 config:invalid signature错误,又是排查签名无误,而且当前页面的url和后台获取到的url也是一致的,焦头烂额,结果发现不是代码的问题,是运维Nginx配置上的问题。
内部测试环境是http请求,但为了方面微信有些https请求需求,会把http转成https,可是Nginx上并没有证书,只不过是手动的把http字符替换成了https,导致微信验证签名失败。
看来微信验证签名时如果有https请求是要有真实证书的,否则会出错
整理了几百本各类技术电子书和视频课程 ,送给小伙伴们。同名公号内回【666】自行领取。和一些小伙伴们建了一个技术交流群,一起探讨技术、分享技术资料,旨在共同学习进步,如果感兴趣就扫码加入我们吧!