【JS-11】如何进行微信JSSDK开发?

分享人:黄震

目录

1.背景介绍

2.知识剖析

3.常见问题

4.解决方案

5.编码实战

6.扩展思考

7.参考文献

8.更多讨论

1.背景介绍

微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。

通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫等微信特有的能力,为微信用户提供更优质的网页体验。

2.知识剖析

微信JS-SDK使用步骤

在使用微信JS-SDK对应的JS接口前,需确保已获得使用对应JS接口的权限

步骤一:引入JS文件

在需要调用JS接口的页面引入如下JS文件,(支持https): http://res.wx.qq.com/open/js/jweixin-1.2.0.js

步骤二:通过 config 接口注入权限验证配置

所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用, Android6.2之前微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题在Android6.2中修复)。


wx.config({
    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: '', // 必填,企业号的唯一标识,此处填写企业号corpid
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: '', // 必填,生成签名的随机串
    signature: '',// 必填,签名,见附录1
    jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});

步骤三:通过ready接口处理成功验证

                    
wx.ready(function(){
    /* config信息验证后会执行ready方法,所有接口调用都必须在config接口
    获得结果之后,config是一个客户端的异步操作,所以如果需要在
    页面加载时就调用相关接口,则须把相关接口放在ready函数
    中调用来确保正确执行。对于用户触发时才调用的接口,
     则可以直接调用,不需要放在ready函数中。*/
});
                    
                

步骤四:通过error接口处理失败验证


wx.error(function(res){
    /* */
    /* 具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,
    对于SPA可以在这里更新签名。*/
});
                    

接口调用说明

所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数:

  • success:接口调用成功时执行的回调函数。
  • fail:接口调用失败时执行的回调函数。
  • complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。
  • cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。
  • trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。

注意:不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回。

以上几个函数都带有一个参数,类型为对象,其中除了每个接口本身返回的数据之外,还有一个通用属性errMsg,其值格式如下:

  • 调用成功时:"xxx:ok" ,其中xxx为调用的接口名
  • 用户取消时:"xxx:cancel",其中xxx为调用的接口名
  • 调用失败时:其值为具体错误信息

3.常见问题

1、如何判断当前客户端版本是否支持指定JS接口?

2、如何在JS文件中调用API?

4.解决方案

1、如何判断当前客户端版本是否支持指定JS接口?


    wx.checkJsApi({
        jsApiList: ['chooseImage'] // 需要检测的JS接口列表,所有JS接口列表见附录2,
        success: function(res) {
            // 以键值对的形式返回,可用的api值true,不可用为false
            // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
        }
    });
备注:checkJsApi接口是客户端6.0.2新引入的一个预留接口,第一期开放的接口均可不使用checkJsApi来检测。

2、如何在JS文件中调用API?

所有的api都封装在一个对象里(wx或jWeixin),通过调用该对象的方法即可,如调用分享到朋友圈接口:


wx.onMenuShareTimeline({
    title: '', // 分享标题
    link: '', // 分享链接
    imgUrl: '', // 分享图标
    success: function () {
        // 用户确认分享后执行的回调函数
    },
    cancel: function () {
        // 用户取消分享后执行的回调函数
    }
});
获取“分享到朋友圈”按钮点击状态及自定义分享内容接口

5.编码实战

官方提供的JSSDK DEMO (html部分比较简单,主要是js部分):

配置权限以及需要调用的接口:


      wx.config({
          debug: false,
          appId: 'wxf8b4f85f3a794e77',
          timestamp: 1421142450,
          nonceStr: '9hKgyCLgGZOgQmEI',
          signature: 'bf7a5555f9ad0e7e491535f232349a40510a6f8f',
          jsApiList: [
                    'checkJsApi',
                    'onMenuShareTimeline',
                    'chooseImage',
                    'previewImage',
                    'uploadImage',
                    'downloadImage',
                     'getLocation',]
                    })
                    
                

接口的调用(这里调用分享朋友圈、图片选择、拍照上传、下载、地理位置获取等接口):

wx.ready(function () {
      // 1 判断当前版本是否支持指定 JS 接口,支持批量判断
      document.querySelector('#checkJsApi').onclick = function () {
        wx.checkJsApi({
          jsApiList: [
            'getNetworkType',
            'previewImage'
          ],
          success: function (res) {
            alert(JSON.stringify(res));
          }
        });
      };
       // 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口
      document.querySelector('#onMenuShareTimeline').onclick = function () {
        wx.onMenuShareTimeline({
          title: '互联网之子',
          link: 'http://movie.douban.com/subject/25785114/',
          imgUrl: 'http://img3.douban.com/view/movie_poster_cover/spst/public/p2166127561.jpg',
          trigger: function (res) {
            alert('用户点击分享到朋友圈');
          },
          success: function (res) {
            alert('已分享');
          },
          cancel: function (res) {
            alert('已取消');
          },
          fail: function (res) {
            alert(JSON.stringify(res));
          }
        });
        alert('已注册获取“分享到朋友圈”状态事件');
      };
                 // 5 图片接口
      // 5.1 拍照、本地选图
      var images = {
        localId: [],
        serverId: []
      };
      document.querySelector('#chooseImage').onclick = function () {
        wx.chooseImage({
          success: function (res) {
            images.localId = res.localIds;
            alert('已选择 ' + res.localIds.length + ' 张图片');
          }
        });
      };
      // 5.2 图片预览
      document.querySelector('#previewImage').onclick = function () {
        wx.previewImage({
          current: 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg',
          urls: [
            'http://img3.douban.com/view/photo/photo/public/p2152117150.jpg',
            'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg',
            'http://img3.douban.com/view/photo/photo/public/p2152134700.jpg'
          ]
        });
      };
      // 5.3 上传图片
      document.querySelector('#uploadImage').onclick = function () {
        if (images.localId.length == 0) {
          alert('请先使用 chooseImage 接口选择图片');
          return;
        }
        var i = 0, length = images.localId.length;
        images.serverId = [];
        function upload() {
          wx.uploadImage({
            localId: images.localId[i],
            success: function (res) {
              i++;
              alert('已上传:' + i + '/' + length);
              images.serverId.push(res.serverId);
              if (i < length) {
                upload();
              }
            },
            fail: function (res) {
              alert(JSON.stringify(res));
            }
          });
        }
        upload();
      };
      // 5.4 下载图片
      document.querySelector('#downloadImage').onclick = function () {
        if (images.serverId.length === 0) {
          alert('请先使用 uploadImage 上传图片');
          return;
        }
        var i = 0, length = images.serverId.length;
        images.localId = [];
        function download() {
          wx.downloadImage({
            serverId: images.serverId[i],
            success: function (res) {
              i++;
              alert('已下载:' + i + '/' + length);
              images.localId.push(res.localId);
              if (i < length) {
                download();
              }
            }
          });
        }
        download();
      };
      // 7 地理位置接口
      // 7.1 查看地理位置
      document.querySelector('#openLocation').onclick = function () {
        wx.openLocation({
          latitude: 23.099994,
          longitude: 113.324520,
          name: 'TIT 创意园',
          address: '广州市海珠区新港中路 397 号',
          scale: 14,
          infoUrl: 'http://weixin.qq.com'
        });
      };
      // 7.2 获取当前地理位置
      document.querySelector('#getLocation').onclick = function () {
        wx.getLocation({
          success: function (res) {
            alert(JSON.stringify(res));
          },
          cancel: function (res) {
            alert('用户拒绝授权获取地理位置');
          }
        });
      };

6.扩展思考

编码实战中使用微信JSSDK时可能会遇到什么问题?

1、用户授权问题:
涉及到用户的个人隐私,所以,在调用接口获取用户数据时,需要用户提供授权,只有授权通过时才可以成功获得用户信息。

2、获取用户地理位置信息?
除了需要用户授权之外,通过接口获取的用户信息并不是地址信息,而是地理的经纬信息,若需要拿到具体的地址,还得用经纬信息通过腾讯地图API或百度地图API等来获取详细地址。

具体地址的转换(腾讯地图为例,需引入qqmap.js文件,网络应用:http://map.qq.com/api/js?v=2.exp):如


             wx.ready(function () {//获取详细经纬度后反查腾讯地图得到确切地址
              wx.getLocation({
                type: 'wgs84',
                success: function (res) {
                  // 地址解析:http://lbs.qq.com/javascript_v2/guide-service.html#link-four
                  let geocoder = new qq.maps.Geocoder({
                    complete: function (result) {
                      resolve(result.detail.address)
                    }
                  })
                  var coord = new qq.maps.LatLng(res.latitude, res.longitude)
                  geocoder.getAddress(coord)
                }
              })
            })
                    //正则匹配已开通服务城市,匹配成功跳到具体城市
               wx.getLocation().then(function (res) {
                for (let i in self.cities) {
                  let city = self.cities[i]
                  if (city.parent === 0) {
                    continue
                  }
                  let patt = new RegExp(city.name)
                  if (patt.test(res)) {
                    self.city = [city.parent, city.value]
                    return
                  }
                }
              })
                

使用百度地图可能会由误差,需进行转化经纬数据在调用百度mapAPI

7.参考文献

参考一: 微信开发者接口文档

参考二: 微信JS-SDK Demo

8.更多讨论

前端是否能完成权限配置?

不考虑安全问题,应该是可以的,非常不推荐

第一步 获取access_token

第二步 获取jsapi_ticket

第三步 参数名排序和拼接字符串,并加密(sha1)

其思想可以参考后端java代码,前两部异步请求(不能用jsonp,返回格式不对,可配置nginx跨域)


public static String SHA1(String decript) { //sha1加密算法
    try {
        MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
        digest.update(decript.getBytes());
        byte messageDigest[] = digest.digest();
        // Create Hex String
        StringBuffer hexString = new StringBuffer();
        // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}
/**
 *  获取签名
 */
public static void main(String[] args) {
    //1、获取AccessToken
    String accessToken = getAccessToken();

    //2、获取Ticket
    String jsapi_ticket = getTicket(accessToken);

    //3、时间戳和随机字符串
    String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//随机字符串
    String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//时间戳

    System.out.println("accessToken:"+accessToken+"\njsapi_ticket:"+jsapi_ticket+"\n时间戳:"+timestamp+"\n随机字符串:"+noncestr);

    //4、获取url
    String url="http://www.luiyang.com/add.html";
    /*根据JSSDK上面的规则进行计算,这里比较简单,我就手动写啦
    String[] ArrTmp = {"jsapi_ticket","timestamp","nonce","url"};
    Arrays.sort(ArrTmp);
    StringBuffer sf = new StringBuffer();
    for(int i=0;i<'ArrTmp.length';i++){
        sf.append(ArrTmp[i]);
    }
    */

    //5、将参数排序并拼接字符串
    String str = "jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;

    //6、将字符串进行sha1加密
    String signature =SHA1(str);
    System.out.println("参数:"+str+"\n签名:"+signature);
}

鸣谢

感谢大家观看

BY :李绍博 庄引 | 黄震

Contact GitHub API Training Shop Blog About © 2016 GitHub, Inc. Terms Privacy Security Status He