微信JS-SDK分享接口实例开发及本地测试(nodeJS)

目录
| - 1.Node环境搭建
| - 2.JS-SDK使用说明
| - 3.实例开发
  | - 1)获取access_token
  | - 2)获取jsapi_ticket
  | - 3)计算signature
  | - 4)前端调用
  | - 5)配置测试
| - 6)优化请求
| - 4.常见问题
  | - 1)config错误
  | - 2)signature错误
  | - 3)url domain错误
  | - 4)其他错误
| - 5.本地测试,使用内网穿透

1.Node环境搭建

1.1创建项目

mkdir  项目名称

1.2初始化项目

npm init

在项目目录下会生成package.json文件

1.3安装express

npm install express

1.4创建入口文件,在根目录下新建index.js

var express = require("express");
var app = express()
app.get('/',function(req,res){
   res.send('hello world')
})
app.listen(3030, function(){
  console.log("服务启动")
})

1.5 配置启动命令==>在package.json中scripts添加


  "scripts": {
    "dev": "node index.js"
  },

在终端中进入目录执行npm run dev即可启动
在地址栏中输入http://localhost:3030/
在这里插入图片描述
为了避免每次修改需要重启,可以使用自动重启工具nodemon,具体使用方法查看文章nodemon --NODE自动重启工具

执行到这一步,node环境就搭建好了

接下来第二,三,四部分转载自:https://www.jianshu.com/p/1a35e1dbe1ad

2.JS-SDK使用说明

微信JS-SDK说明文档传送门:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
阅读前3点即可实现我们想要的分享功能。如下:

  • 绑定域名,配置“JS接口安全域名” (在本地测试没有域名如何测试会在第五部分讲解)
  • 引入js文件
<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
或
<script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
  • 通过config接口注入权限验证配置
wx.config({
  debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
  appId: '', // 必填,公众号的唯一标识
  timestamp: , // 必填,生成签名的时间戳
  nonceStr: '', // 必填,生成签名的随机串
  signature: '',// 必填,签名
  jsApiList: [] // 必填,需要使用的JS接口列表
});
其中,最重要的就是signature签名的生成。

 1. 生成"signature"需要"jsapi_ticket"
 2. 生成"jsapi_ticket"需要"access_token"
 3. 还有7200秒过期等规则,组装规则

  • 通过ready接口处理成功验证
wx.ready(function(){
  // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
  • 通过error接口处理失败验证
wx.error(function(res){
  // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});

整个流程解释:

  1. 申请公众号,这样就有appID和appSecret(在开发/基本配置分类下)
  2. 引入js文件http://res.wx.qq.com/open/js/jweixin-1.6.0.js,去使用微信JS-SDK
  3. 获取access_token,配置IP白名单才能调此接口根据appID和appSecret进行请求(有效期7200秒,每次使用前检查,过期重新获取)
  4. 获取jsapi_ticket,根据上一步的access_token进行请求(有效期72000秒,每次使用之前检查,过期重新获取
  5. 计算signature,根据上一步的jsapi_ticket
    1)排序,参与签名的字段:noncestr(随机字符串),有效的jsapi_ticket,timestamp(时间戳),URL(不带#后面部分的要分享的url),字典序从小到大排序
    2)拼接,使用URL键值对的格式冰洁字符串string1,参数名必须为小写字符
    3)加密,对string2作sha1加密,字段名和字段值都采用原始值,不进行URL转义
    注:计算签名必须在服务端完成签名,返回前端
  6. 添加JS安全接口域名(在公众号后台的设置/公众号设置/功能设置中添加),这里需要MP_verify_AwmmQFM5B0vHg035.txt文件检查功能
  7. 部署测试,注意事项:80端口,域名已备案。(本地测试
    ,无备案域名,该如何,后续会讲解 )

3.实例开发

官方 DEMO页面和例子传送门:附录6
如果你有可以使用的公众号,那么直接用就好了。如果没有,就需要使用测试公众号。在“公众号后台的开发/开发者工具/公众平台测试帐号”,登陆进入就可以使用测试公众号了
1)获取access_token

// 这里应该判断是否存在签名,是否已过期
//(稍后添加)
// ...
// 公众号字段
var appID = "你的appID";
var appSecret = "你的appSecret";
// 获取access_token
var tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='+appID+'&secret='+appSecret;
request(tokenUrl, function (error, response, body) {
  if (response.statusCode === 200) {
    body = JSON.parse(body); 
    // 这里我缓存到了global
    global.wxshare.access_token = body.access_token;
    // 获取jsapi_ticket
    // ...
}

2)获取jsapi_ticket

var ticketUrl = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + body.access_token + '&type=jsapi';
request(ticket, function (err, response, content) {
  content = JSON.parse(content);
  if (content.errcode == 0) {
    // 这里我缓存到了global
    global.wxshare.jsapi_ticket = content.ticket;
    // 计算signature
    // ...
  }
})

3)计算signature

// 计算signature
// 先拿一个当前时间戳,这里我缓存到了global
global.wxshare.deadline = new Date().getTime();
// 通过调用计算签名方法
var signatureStr = sign(content.ticket, req.body.url);
// 当前时间戳
signatureStr.deadline = new Date().getTime();
// 缓存签名
if (signindex && signindex !== 0) {
  global.wxshare.signs(signindex, 1, signatureStr);
} else {
  global.wxshare.signs.push(signatureStr);
}
// 返回给页面
res.status(200).json(signatureStr);

生成签名

// 随机字符串
var createNonceStr = function () {
  return Math.random().toString(36).substr(2, 15);
};

// 时间戳
var createTimestamp = function () {
  return parseInt(new Date().getTime() / 1000) + '';
};

// 排序拼接
var raw = function (args) {
  var keys = Object.keys(args);
  keys = keys.sort()
  var newArgs = {};
  keys.forEach(function (key) {
    newArgs[key.toLowerCase()] = args[key];
  });
  var string = '';
  for (var k in newArgs) {
    string += '&' + k + '=' + newArgs[k];
  }
  string = string.substr(1);
  return string;
};

/**
* @synopsis 签名算法 
*
* @param jsapi_ticket 用于签名的 jsapi_ticket
* @param url 用于签名的 url ,注意必须动态获取,不能 hardcode
*
* @returns
*/
var sign = function (jsapi_ticket, url) {
  var ret = {
    jsapi_ticket: jsapi_ticket,
    nonceStr: createNonceStr(),
    timestamp: createTimestamp(),
    url: url
  };
  var string = raw(ret);
      jsSHA = require('jssha');
      shaObj = new jsSHA(string, 'TEXT');
  ret.signature = shaObj.getHash('SHA-1', 'HEX');

  return ret;
};
module.exports = sign;

检查页面链接对应的签名是否可用

//检查页面链接对应的签名是否可用
var signtag = false;
var signindex;
// 检查签名
global.wxshare.signs.forEach(function (item, index) {
  if (item.url === req.body.url) {
    signindex = index;
    if (item.deadline && new Date().getTime() - item.deadline < 6000000) {
      signtag = true;
    }
  }
});
//当签名不可用时,检测jsapi_ticket是否可用,来决定是直接请求签名还是先请求jsapi_ticket再请求签名
if (!signtag) {
   // 请求操作
   // 计算签名操作
   // ...
}
// 相应路由
app.post('/signture', Base.wxconfig);
// 对应方法
exports.wxconfig = function(req,res,next) {
   // ...
}

4)前端调用

<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script>
  // 请求签名
  $.ajax({
    url: "/signture",
    type: 'post',
    data: { url: location.href.split('#')[0] },
    success:function(res){
      wx.config({
        debug: false,
        appId: 'wxa2c416de84300ee5',
        timestamp: res.timestamp,
        nonceStr: res.nonceStr,
        signature: res.signature,
        jsApiList: [
          'checkJsApi',
          'onMenuShareTimeline',
          'onMenuShareAppMessage',
          'onMenuShareQQ'
        ]
      }); 
      wx.ready(function () {
        var shareData = {
          title: document.title,
          desc: getDesc(),
          link: res.url,
          imgUrl: getImage()
        };
        wx.onMenuShareAppMessage(shareData);
        wx.onMenuShareTimeline(shareData);
        wx.onMenuShareQQ(shareData);
      });
      wx.error(function (res) {
        alert(res.errMsg);  // 正式环境记得关闭啊!!!!
      });
    }
  });
  // 获取描述字段方法
  function getDesc() {
    var meta = document.getElementsByTagName("meta");
    for (var i=0;i<meta.length;i++){
      if(typeof meta[i].name!="undefined"&&meta[i].name.toLowerCase()=="description"){
        return meta[i].content;
      }
    }
  };
  // 获取图片
  function getImage() {
    return 'http://'+location.host+'/images/logo.png';
  };
</script>

5)配置测试
添加JS接口安全域名(在公众号后台的设置/公众号设置/功能设置中添加),这里需要实现MP_verify_AwmmQFM5B0vHg035.txt文件检查功能。
因为这个MP_verify_AwmmQFM5B0vHg035.txt文件里,只有一行字符串,直接返回即可

// 相应路由
app.get('/MP_verify_CwPkZ2phenWhKlmR.txt', Base.checkWx);

// 对应方法
exports.checkWx = function(req, res) {
  res.send('MP_verify_AwmmQFM5B0vHg035.txt文件中的内容');
}

6)优化请求(检查微信环境,减少不必要的微信接口调用次数)

<script>
  // 检查微信环境
  function isWeiXin() {
    var ua = window.navigator.userAgent.toLowerCase();
    if (ua.match(/MicroMessenger/i) == 'micromessenger') {
      return true;
    } else {
      return false;
    }
  };
  // 如果为真,则请求
  if(isWeiXin()){
      // 去获取signature签名
      ....
  });
</script>

4.常见问题

官方常见错误查阅传送门:附录5
1)config 错误
如果config fail,一般说明存在配置字段遗漏,或者配置字段的值为空(null,undefined, “”)
2)signature错误
如果报invalid signature 错误,一般说明签名没有生成正确,可以使用微信JS接口签名校验工具:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign,对比代码生成出来的是否和校验工具生成的一样。比如时间戳长度,顺序,拼接遗漏。
3)url domain 错误
如果报invalid url domain 错误,说明配置工作已经ok啦,出现这个的原因是域名没有添加到JS安全接口域名下,或者没有使用80、443端口
注:在微信开发者工具中点击地址栏刷新会触发两次请求,可以使用菜单里面的刷新。

5.本地测试,使用内网穿透

使用Sunny-Ngrok实现内网穿透
Sunny-Ngrok使用教程传送门:http://ngrok.cc/_book/start/ngrok_linux.html
1)开通隧道
①先在Sunny-Ngrok注册一个账号以便开通渠道,注册
②注册成功后进行登录
③选择要开通的服务器,有免费的可选择
在这里插入图片描述
⑤开通渠道
在这里插入图片描述

在上图中表单信息解释

隧道名称:可以随便填写,无关紧要只是为了一个备注
前置域名:服务器免费赠送的域名,请不要带上后缀,如果要 sunny.free.idcfengye.com 只需要填写 sunny 即可
本地端口:可以为同一个局域网内任意一台机器进行映射,只需要填对ip和端口就行,例如:192.168.1.1:80
http验证用户名:非必填项,在需要的时候填写,否则可以不填
http验证密码:非必填项,在需要的时候填写,否则可以不填
⑥成功后可以在隧道管理中看到刚刚申请的记录
在这里插入图片描述
2)启动隧道
①根据自己的操作系统去下载对应的 客户端
②启动隧道(隧道id即第⑥步中的隧道id)(以mac为例,window可看官方文档)
启动单个隧道

./sunny clientid 隧道id

启动多个隧道

./sunny clientid 隧道id,隧道id

注:启动隧道,需先在终端下进入第⑦步下载的客户端目录下,比如:我刚刚下载的客户端在/Users/wj/Desktop下,在终端下执行cd /Users/wj/Desktop
启动成功后页面:
在这里插入图片描述
3)添加JS安全域名(在公众号后台的设置/公众号设置/功能设置中)
在这里插入图片描述
4)使用微信开发者工具即可查看微分享是否成功
在这里插入图片描述
以上便是微信JS-SDK分享接口实例开发及本地测试全过程

热门文章

暂无图片
编程学习 ·

从入门到删库跑路的过程

数据库简介数据库的发展史萌芽阶段:文件系统使用磁盘文件来存储数据初级阶段:第一代数据库出现了网状模型、层次模型的数据库中级阶段:第二代数据库关系型数据库和结构化查询语言高级阶段:新一代数据库关系-对象 型数据库NoSQL非关系数据库:Not Only SQL 数据库管理系统 数…
暂无图片
编程学习 ·

理解持续测试,才算理解DevOps

软件产品的成功与否,在很大程度上取决于对市场需求的及时把控,采用DevOps可以加快产品交付速度,改善用户体验,从而有助于保持领先于竞争对手的优势。作为敏捷开发方法论的一种扩展,DevOps强调开发、测试和运维不同团队间的协作与沟通。持续集成和持续测试是一个在迭代中构…
暂无图片
编程学习 ·

NLP 任务中有哪些巧妙的 idea?

文章目录1. 分布式假设(Distributional Hypothesis)2. 词袋模型(Bag-of-Words)3. 潜在语义分析(Latent Semantic Analysis)4. 概率主题模型(Probabilistic Topic Models )5. 基于BMES的中文分词或基于BIO的NER/Chunking6. 基于PageRank的TextRank转载来源:https://www…
暂无图片
编程学习 ·

3、【STM32F0系列学习】之—中断和事件

【STM32F0系列学习】之—中断和事件1、什么是“中断”2、中断优先级3、中断嵌套4、嵌套向量中断控制器 (NVIC)5、中断与事件的区别和主要特性6、外部中断(EXTI)配置6.1【标准库】的配置方式6.2【HAL库】的配置方式 1、什么是“中断”CPU执行程序时,由于发生了某种随机的事件…
暂无图片
编程学习 ·

Android编程权威指南总结(六)

第十七章 双版面主从用户界面本章是为了适应平板设备。双版面主从用户界面,也就是平板上的列表和详情界面同时展示的情况。一、增加布局灵活性双版面布局里面,一个 Activity 托管两个 Fragment。1、方法上使用 @LayoutRes 注解,这告诉Android Studio,任何时候该注解的…
暂无图片
编程学习 ·

spring+mybatis日志

spring4默认日志是log4j, spring5默认日志是JUL spring4下使用JCL时,如果有log4j的jar,用的具体实现类是log4j,否则用的具体实现类是JUL spring4下使用JCL时,用的具体实现类是JUL1、spring4下日志加载顺序//循环for(int i=0; i<classesToDiscover.length && resu…
暂无图片
编程学习 ·

功能测试框架

功能测试框架可以包括:界面友好性测试、功能测试、链接测试、容错测试、稳定性测试、常规性能测试、配置测试、算法测试等等。1.1.1 界面友好性测试风格、样式、颜色是否协调界面布局是否整齐、协调(保证全部显示出来的,尽量不要使用滚动条界面操作、标题描述是否恰当(描述…
暂无图片
编程学习 ·

爬虫工作的代理ip选择

代理ip的使用是爬虫工作必须使用的爬取辅助工具,大数据的快速发展,很多的网站不断的维护自己的网站信息,开始设置反爬虫机制,在网站进行反爬虫限制的情况下,怎样通过反爬虫机制,提高工作效率。一:使用多线程与代理ip1、多线程方式:多线程同时开展工作采集,迅速提高工作…
暂无图片
编程学习 ·

程序员翻车时的 30 种常见反应!

**软件开发工作充满了挑战性。人无完人,对于程序员来说,写出有 bug 的代码是在所难免的。有些人很淡定,也有一些人会感到生气、沮丧、不安或气馁。在修复 bug 的过程中我们都经历了什么?这个值得我们一探究竟。 本文列出了程序员在修复 bug 时可能会说的一些话或者想法。我…
暂无图片
编程学习 ·

Mysql 5.7实现存在则更新,不存在则新增

需求:如果表中存在某行,那么更新即可;不存在某行,那么就新增一条。通常是将主键索引或唯一索引作为判断条件。思路:可以使用Mysql的INSERT ... ON DUPLICATE KEY UPDATE或REPLACE或UPDATE实现。如果希望一条语句实现,可以考虑前两种实现创建一张表,表中包含自增Id和唯一…
暂无图片
编程学习 ·

设计模式-工厂模式

关注公众号 JavaStorm 获取更多精彩工厂模式定义 工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化…
暂无图片
编程学习 ·

利用Spring提供的事务监听ApplicationEvent完成事件

前段时间开发项目时,碰到一个数据库事务还没提交,但是发送MQ已经被消费者消费,导致了数据不同步问题。具体是这样子的,一般我们会在@Service类中去处理数据库的操作及其他服务处理,一般都会在涉及到数据库的增删改的方法上添加@Transactional注解,表示这个方法被托管给sp…
暂无图片
编程学习 ·

windows10系统-2-安装Nodejs及SocketIO

(1)双击node-v12.14.1-x64.msi CMD>npm --version查看npm的版本 CMD>npm -v (2)使用淘宝镜像的命令 CMD>npm install -g cnpm --registry=https://registry.npm.taobao.org CMD>npm list -g查看所有全局安装的模块 【全局安装所在路径C:\Users\user\AppData\Roamin…
暂无图片
编程学习 ·

App测试中IOS和安卓测试的区别

一、分辨率的测试 安卓端有20多种,IOS相对就比较少一些 二、操作系统的版本 安卓的操作系统比较多,IOS比较少,而且它只能支持单项升级,不能支持降级。 三、操作习惯的一些不同 安卓习惯点击BACK键,所以我们要测试一下BACK键,看看BACK键是否被重写了。点击BACK键之后,看看…
暂无图片
编程学习 ·

css border-radius的用法及自适应的椭圆

我们知道border-radius允许您为元素添加圆角边框! 而border-radius 属性是一个简写属性,用于设置四个 border-*-radius 属性。 如果省略 bottom-left,则与 top-right 相同。如果省略 bottom-right,则与 top-left 相同。如果省略 top-right,则与 top-left 相同。 先看个例子…
暂无图片
编程学习 ·

菜鸟小白怎样才能学好Java!

随着Java语言的发展,Java编程受到的欢迎度也在逐渐增热。越来越多的人会选择从事Java程序员这一职业,那么应该如何学Java呢?怎样才能学好Java呢?下面上海千锋带你一起了解!1、认真阅读Java相关的书籍虽然现在在网上有大量的视频可以观看,使得Java的学习变得简单了起来,但…