黑马程序员技术交流社区
标题:
【上海校区】前端对接微信分享功能完全指南
[打印本页]
作者:
不二晨
时间:
2018-11-9 09:00
标题:
【上海校区】前端对接微信分享功能完全指南
背景
最近,由于公司业务需要,接入了
微信web端分享
接口。虽然微信的接口文档已经很详细了,但是缺少实战代码。小编搜了一下掘金网站好像也很少这方面的分享(或许是太过简单,大神们都不屑于分享这类经验。当然也有客观原因,现在大多都做小程序了,微信web端的流量被分流了很多。
准备
此为
微信公众平台接口文档
地址
此为
微信公众平台接口测试帐号申请
地址
此为
获取基础access_token
地址
此为
微信JS-SDK说明文档
地址
以下nodejs端是基于egg框架。
开发
1、申请公众号或测试版公众号
公众号申请(这里不详说公众号申请,有需要自己去官网看文档)用于生产环境的开发。
对于前期开发阶段,可以去
微信公众平台接口测试帐号申请
,微信扫描二维码即可申请。
注意事项:
1、填写JS接口安全域名,绑定服务器域名
2、扫描“测试号二维码”,把你的微信号加入列表,然后你的微信就有权限进行测试了。
没有加入列表的微信号是没有权限使用微信接口的
,这里要注意一下。
至此,测试公众号配置完成。
2、获取基础
access_token
接口。
微信有两个
access_token
,一个是基础
access_token
,一个是网页授权
access_token
,具体区别,如图所述:
我们调用的微信分享接口只需要基础
access_token
就好。
接口api:
(get)
api.weixin.qq.com/cgi-bin/tok…
参数:
grant_type:填写“client_credential”
appid: 公众号appId
secret: 公众号secret
返回值:
成功时
{ "access_token":"ACCESS_TOKEN", "expires_in":7200}复制代码expires_in为该access_token的有限时间,由于微信对于获取accesss_token接口每天有次数限制,所以我们需要把access_token存到服务器里,等到其失效后再重新发起请求。
异常时
{ "errcode":错误码, "errmsg":错误信息}复制代码
错误码:
-1 系统繁忙,此时请开发者稍候再试
0 请求成功
40001 AppSecret错误或者AppSecret不属于这个公众号,请开发者确认AppSecret的正确性
40002 请确保grant_type字段值为client_credential
40164 调用接口的IP地址不在白名单中,请在接口IP白名单中进行设置
注意:
1、当遇到错误码为
40164
(调用接口的IP地址不在白名单中,请在接口IP白名单中进行设置),要去到公众号的白名单列表加上自己服务器的IP,
测试公众号账号是不用配置白名单的
,所以在
把测试公众号
参数换成
正式公众号
参数时,记得配置白名单。
代码片段:
const { ctx, config } = this; let timestamp = new Date().valueOf(); // 判断缓存里是否有access_token且没有过期,并且获取该access_token时的appId与现在的一致,判断appId是为了避免切换不同公众号配置时没有清缓存出现错误 // 如果以上条件不能同时满足,则重新请求access_token if (!ctx.session.tokenObj || ctx.session.tokenObj.expires_in < timestamp || ctx.session.tokenObj.app_id !== config.wx.appId) { const tokenResult = await ctx.curl(`
https://api.weixin.qq.com/cgi-bi ... edential&appid=
${config.wx.appId}&secret=${config.wx.secret}`, { dataType: 'json' }); if (tokenResult.status === 200 && tokenResult.data && tokenResult.data.access_token) { ctx.session.tokenObj = { access_token: tokenResult.data.access_token, expires_in: timestamp + tokenResult.data.expires_in * 1000, app_id: config.wx.appId }; } else { // 记录请求错误日志,方便定位错误 // 因为该缓存access_token已经不能使用,请求错误时记得把access_token缓存也清空。 ctx.session.tokenObj = null; ctx.logger.error(new Error(`wxconfig: ${JSON.stringify(config.wx)}`)); ctx.logger.error(new Error(`tokenResult: ${JSON.stringify(tokenResult)}`)); } }复制代码
3、获取jsapi_ticket
接口api:
(get)
api.weixin.qq.com/cgi-bin/tic…
参数:
access_token:上一步请求返回的access_token
type:'jsapi'
返回值:
成功时:
{"errcode":0,"errmsg":"ok","ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA","expires_in":7200}复制代码与access_token一样,expires_in为该ticket的有限时间,由于微信对于获取ticket接口每天有次数限制,所以我们需要把access_token存到服务器里,等到其失效后再重新发起请求。
异常时:
{"errcode":42001,"errmsg":"access_token expired hint: [5bugDA09718938!]"}复制代码
代码:
timestamp = new Date().valueOf();// 判断缓存里是否有jsapi_ticket且没有过期,并且获取该jsapi_ticket时的appId与现在的一致,判断appId是为了避免切换不同公众号配置时没有清缓存出现错误// 如果以上条件不能同时满足,则重新请求access_tokenif (!ctx.session.jsapiObj || ctx.session.jsapiObj.expires_in < timestamp || ctx.session.jsapiObj.app_id !== config.wx.appId) { const jsapiResult = await ctx.curl(`
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=
${ctx.session.tokenObj.access_token}&type=jsapi`, { dataType: 'json' }); if (jsapiResult.status === 200 && jsapiResult.data && jsapiResult.data.errcode === 0) { ctx.session.jsapiObj = { ticket: jsapiResult.data.ticket, expires_in: timestamp + jsapiResult.data.expires_in * 1000, app_id: config.wx.appId }; ctx.logger.error(new Error(`jsapiResult:success: ${JSON.stringify(jsapiResult)}`)); } else { // 记录请求错误日志,方便定位错误 // 因为该缓存jsapi_ticket已经不能使用,请求错误时记得把jsapi_ticket缓存也清空。 ctx.session.jsapiObj = null; ctx.logger.error(new Error(`wxconfig: ${JSON.stringify(config.wx)}`)); ctx.logger.error(new Error(`jsapiResult: ${JSON.stringify(jsapiResult)}`)); }}复制代码我发现,这个api基本不会怎么出现错误码,基本上access_token如果没有问题,这个api的调用也不会报错。
常出现的错误多是上一步的access_token缓存策略不合理导致这个接口的access_token参数的值不是有效的access_token。
4、获取签名算法signature值
签名生成规则如下:
参与签名的字段包括
有效的jsapi_ticket、
noncestr(随机字符串)、
timestamp(时间戳)、
url(当前网页的URL,不包含#及其后面部分) 。
对所有待签名参数
按照字段名的ASCII 码从小到大排序
(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是
所有参数名均为小写字符
。对string1作
sha1加密
,字段名和字段值都采用原始值,
不进行URL 转义
。
代码:
const jsapi_ticket = ctx.session.jsapiObj.ticket;const uuidv1 = require('uuid/v1');const noncestr = uuidv1();timestamp = new Date().valueOf();const { url } = ctx.query;const string1 = `jsapi_ticket=${jsapi_ticket}&noncestr=${noncestr}×tamp=${timestamp}&url=${url}`;const crypto = require('crypto');const hash = crypto.createHash('sha1');hash.update(string1);const signature = hash.digest('hex');复制代码
注意:
签名用的noncestr和timestamp必须与下一步的wx.config中的nonceStr和timestamp相同。
签名用的url必须是
调用JS接口页面的完整URL
。
出于
安全
考虑,建议在
服务器端
实现签名逻辑。
参数先后顺序就jsapi_ticket、noncestr、timestamp、url,这个顺序搞错会导致签名
签名算法这一步很关键,不合理会导致下一步出现错误。具体的错误相对应的解决办法可以查看官方文档,里面很详细。地址:
微信JS-SDK说明文档
,查看该网站的
附录5-常见错误及解决方法
以上步骤都是node端实现,以下为web端的代码。
5、引入微信js文件
官方js地址:
res.wx.qq.com/open/js/jwe…
官方js备用地址:
res2.wx.qq.com/open/js/jwe…
6、通过wx.config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: '', // 必填,公众号的唯一标识 timestamp: , // 必填,生成签名的时间戳,与生成签名的timestamp要一致 nonceStr: '', // 必填,生成签名的随机串,与生成签名的nonceStr要一致 signature: '',// 必填,签名 jsApiList: [] // 必填,需要使用的JS接口列表});复制代码jsApiList具体可查看:
微信JS-SDK说明文档
,查看该网站的
附录2-所有JS接口列表
7、调用微信分享接口
代码:
wx.ready(function() { const title = '分享标题'; const desc = '分享描述'; const imgUrl = '分享图片链接'; // 朋友圈 wx.onMenuShareTimeline({ title: title, // 分享标题 link: url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: imgUrl // 分享图标 }); // 微信朋友 wx.onMenuShareAppMessage({ title: title, // 分享标题 desc: desc, // 分享描述 link: url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: imgUrl, // 分享图标 type: 'link', // 分享类型,music、video或link,不填默认为link dataUrl: '' // 如果type是music或video,则要提供数据链接,默认为空 }); // qq wx.onMenuShareQQ({ title: title, // 分享标题 desc: desc, // 分享描述 link: url, // 分享链接 imgUrl: imgUrl // 分享图标 }); // qq空间 wx.onMenuShareQZone({ title: title, // 分享标题 desc: desc, // 分享描述 link: url, // 分享链接 imgUrl: imgUrl // 分享图标 }); // 腾讯微博 wx.onMenuShareWeibo({ title: title, // 分享标题 desc: desc, // 分享描述 link: url, // 分享链接 imgUrl: imgUrl // 分享图标 }); });复制代码另外还有一些调用成功success事件、调用失败fail事件、用户点击取消分享cancel事件。具体可看:
微信JS-SDK说明文档
-
JSSDK使用步骤
-
接口调用说明
至此,微信分享接口已经可用了。希望对刚接触微信接口的你有帮助。
全部代码
// NODE端 async getWXApiTicket() { const { ctx, config } = this; let timestamp = new Date().valueOf(); if (!ctx.session.tokenObj || ctx.session.tokenObj.expires_in < timestamp || ctx.session.tokenObj.app_id !== config.wx.appId) { const tokenResult = await ctx.curl(`
https://api.weixin.qq.com/cgi-bi ... edential&appid=
${config.wx.appId}&secret=${config.wx.secret}`, { dataType: 'json' }); if (tokenResult.status === 200 && tokenResult.data && tokenResult.data.access_token) { ctx.session.tokenObj = { access_token: tokenResult.data.access_token, expires_in: timestamp + tokenResult.data.expires_in * 1000, app_id: config.wx.appId }; } else { ctx.session.tokenObj = null; ctx.logger.error(new Error(`wxconfig: ${JSON.stringify(config.wx)}`)); ctx.logger.error(new Error(`tokenResult: ${JSON.stringify(tokenResult)}`)); } } let res = { code: 500, msg: '获取失败' }; if (ctx.session.tokenObj && ctx.session.tokenObj.app_id === config.wx.appId) { timestamp = new Date().valueOf(); if (!ctx.session.jsapiObj || ctx.session.jsapiObj.expires_in < timestamp || ctx.session.jsapiObj.app_id !== config.wx.appId) { const jsapiResult = await ctx.curl(`
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=
${ctx.session.tokenObj.access_token}&type=jsapi`, { dataType: 'json' }); if (jsapiResult.status === 200 && jsapiResult.data && jsapiResult.data.errcode === 0) { ctx.session.jsapiObj = { ticket: jsapiResult.data.ticket, expires_in: timestamp + jsapiResult.data.expires_in * 1000, app_id: config.wx.appId }; } else { ctx.session.jsapiObj = null; ctx.logger.error(new Error(`wxconfig: ${JSON.stringify(config.wx)}`)); ctx.logger.error(new Error(`jsapiResult: ${JSON.stringify(jsapiResult)}`)); } } if (ctx.session.jsapiObj && ctx.session.jsapiObj.app_id === config.wx.appId) { const jsapi_ticket = ctx.session.jsapiObj.ticket; const uuidv1 = require('uuid/v1'); const noncestr = uuidv1(); timestamp = new Date().valueOf(); const { url } = ctx.query; const string1 = `jsapi_ticket=${jsapi_ticket}&noncestr=${noncestr}×tamp=${timestamp}&url=${url}`; const crypto = require('crypto'); const hash = crypto.createHash('sha1'); hash.update(string1); const signature = hash.digest('hex'); res = { code: 0, data: { nonceStr: noncestr, timestamp, signature, appId: config.wx.appId, jsapi_ticket, string1 } }; } } ctx.body = res; } // JS端 const wx = window['wx']; const url = location.href.split('#')[0]; $.get('/getWXApiTicket?url=' + encodeURIComponent(url), function(res) { if (res.code === 0) { wx.config({ debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: res.data.appId, // 必填,公众号的唯一标识 timestamp: res.data.timestamp, // 必填,生成签名的时间戳 nonceStr: res.data.nonceStr, // 必填,生成签名的随机串 signature: res.data.signature, // 必填,签名 jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareQZone', 'onMenuShareWeibo'] // 必填,需要使用的JS接口列表 }); wx.ready(function() { const title = '分享标题'; const desc = '分享描述'; const imgUrl = '分享图片链接'; // 朋友圈 wx.onMenuShareTimeline({ title: title, // 分享标题 link: url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: imgUrl // 分享图标 }); // 微信朋友 wx.onMenuShareAppMessage({ title: title, // 分享标题 desc: desc, // 分享描述 link: url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: imgUrl, // 分享图标 type: 'link', // 分享类型,music、video或link,不填默认为link dataUrl: '' // 如果type是music或video,则要提供数据链接,默认为空 }); // qq wx.onMenuShareQQ({ title: title, // 分享标题 desc: desc, // 分享描述 link: url, // 分享链接 imgUrl: imgUrl // 分享图标 }); // qq空间 wx.onMenuShareQZone({ title: title, // 分享标题 desc: desc, // 分享描述 link: url, // 分享链接 imgUrl: imgUrl // 分享图标 }); // 腾讯微博 wx.onMenuShareWeibo({ title: title, // 分享标题 desc: desc, // 分享描述 link: url, // 分享链接 imgUrl: imgUrl // 分享图标 }); }); } });
【转载】
链接:
https://juejin.im/post/5be4238df265da6142736496
作者:
不二晨
时间:
2018-11-14 15:35
~(。≧3≦)ノ⌒☆
作者:
梦缠绕的时候
时间:
2018-11-15 15:09
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2