最近公司开发微信内部的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("&timestamp=").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】自行领取。和一些小伙伴们建了一个技术交流群,一起探讨技术、分享技术资料,旨在共同学习进步,如果感兴趣就扫码加入我们吧!


本文转载:CSDN博客