张家口公众号哪家好
发布日期:2020-01-17 浏览次数:
张家口公众号哪家好,网上关于node开发公众号的资料相当缺乏,本文旨在以node的视角对公众号开发做一个阐述。
公众号简介
公众号类型
目前公众号主要分为三种:服务号、订阅号、小程序;还有企业微信只针对企业用户使⽤用,暂且不算在内。
微信公众平台:https://mp.weixin.qq.com/
名称 |
服务对象 |
业务类型 |
关注后的位置 |
---|---|---|---|
服务号 |
企业 |
任意 |
联系⼈人列列表 |
订阅号 |
个⼈人或媒体 |
信息传播 |
归纳在订阅号 |
小程序 |
企业 |
任意 |
归纳在最近使⽤用 |
服务号:给企业提供用户管理与业务服务的能⼒,实现业务扩张。 订阅号:给个⼈或媒体提供信息传播的能⼒,与读者建⽴更好的沟通。 小程序:作⽤基本同服务号,比服务号H5应用体验更更好,但无法替代非H5的沟通系统,可以实现互补。
对于个⼈而⾔,无论是学习还是维护——个⼈公众号,只要不不涉及⽀支付环节,注册一个订阅号足以。如果需要⽀付功能,那么需要注册服务号,服务号注册时需要企业相关证书。
成为开发者
参考资料 微信开发者工具说明 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455784140 npm 库 wechat和wechat-api,以及微信开发者文档。
如果需要结合⾃身业务进⾏定制,那么就需要申请成为开发者,然后调用微信提供的api结合⾃身业务进⾏扩展。
- 首先在左侧菜单中找到:开发 => 基本配置
- 然后接受同意,成为开发者
- 获取开发者ID(AppID)与开发者密码(AppSecret),并妥善保存(很重要)
- 配置URL⽩白名单,把⾃自⼰己的服务器器IP填上去,保证只有指定的服务器器能获取到access_token(很重 要)
名词解释
- AppID:公众应用唯⼀一身份认证
- AppSecret:公众应⽤密码,需妥善保存
- access_token:调⽤微信接⼝所需的凭证,每个接⼝调用都需要,可通过AppID和AppSecret获 取
- URL白名单:增加获取accesstoken的安全性,当密码泄露时,通过白名单过滤⾮法请求(官方说明:https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&key=1495617578&version=1&lang=zhCN&platform=2)
开通测试账号
测试账号使用文档:https://blog.csdn.net/hzw2312/article/details/69664485
在进⾏公众号开发时,通常会先在测试账号中进⾏开发调试,经测试确认无误后,再把新功能切换到正式账号。 开通测试账号,将具有所有的权限!
- 后台管理左侧菜单 => 开发 => 开发者⼯工具
- 开发者⼯具⻚面 => 公众平台测试账号
2. 记录测试账号的appID与appsecret
3 . 测试账号的服务器配置
4. 测试账号的JS接口安全域名配置
5. 扫⼀扫关注⾃⼰的测试账号,然后会在用户列表⾥展现
6. 创建⼏个模板消息,供将来测试使⽤。
sunny-ngrok实现内网穿透
公众号开发时,总是面临各种上传文件服务器的操作,极其不便。而sunny-ngrok提供了内网穿透功能。它可以把你本机的ip发布到外网。
安装sunny-ngrok实现外网的映射:https://www.ngrok.cc/
注册,登录。
点击隧道管理理,打开"开通隧道"
编辑隧道信息-- 填入隧道名(随便填),前置域名(如www.yyy.baidu.com中的yyy,其实就是在该域名下开了一个前缀给你,因此只要写前缀就行了,选一个别⼈人没有⽤过的),本地映射的端⼝,则 是要和web项⽬目的http访问端⼝对应。
确认开通后回到隧道管理。就拿到了隧道id。
Mac
下载客户端。放到usr/local下。并在此打开命令行:
./sunny clientid d5324b15e9e99905 你的隧道id
看到这个就配置成功了。下面起一个node服务器来验证一下。
npm init npm i koa koa-router koa-static koa-bodyparser -S
// index.js const Koa = require('koa') const Router = require('koa-router') const static = require('koa-static') const bodyParser = require('koa-bodyparser'); const app = new Koa() app.use(bodyParser()) const router = new Router() app.use(static(__dirname + '/')) app.use(router.routes()); /*启动路由*/ app.use(router.allowedMethods()); app.listen(3000);
然后写一个vue的页面:
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0"> <script src="https://unpkg.com/vue@2.1.10/dist/vue.min.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="https://unpkg.com/cube-ui/lib/cube.min.js"></script> <script src="https://cdn.bootcss.com/qs/6.6.0/qs.js"></script> <script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script> <link rel="stylesheet" href="https://unpkg.com/cube-ui/lib/cube.min.css"> <style> /* .cube-btn { margin: 10px 0; } */ </style> </head> <body> <div id="app"> <cube-input v-model="value"></cube-input> <cube-button @click='click'>Click</cube-button> </div> <script> var app = new Vue({ el: '#app', data: { value: 'input' }, methods: { click: function () { console.log('click') } }, mounted: function () { }, }); </script> </body> </html>
输入http://djtao.free.idcfengye.com/。访问成功:
客服消息接口
co-wechat实现消息收发业务
有一个不错的半官方的库 co-wechat
:https://github.com/node-webot/co-wechat 把它安装了。
然后新建一个配置。
// config.js module.exports={ appid:测试号的appid, appsecret:测试号的appsecret, token:你自己定的token }
然后根据co-wechat文档写一个接口:
const config=require('./config') const wechat=require('co-wechat') router.all('/wechat',wechat(config).middleware( async message=>{ console.log('wecaht',message); return `hello world ${message.Content}` } ))
启动服务器。这时候可以配置测试号了。(服务不启动时,无法通过验证)
此时试一试发消息:
后台console的信息是:
原理
以上这个过程是怎么实现的呢?原理必然是重点。
这是服务器验证微信的过程。
首先简单描述一下微信收发信息流程:
假设我们不需要co-WeChat这个库,自己写这个收发流程。可以是这样:
// index.js const Koa = require('koa') const Router = require('koa-router') const static = require('koa-static') const xml2js = require('xml2js') // xml转化为json const app = new Koa() const url = require('url') const conf = require('./conf') const crypto = require('crypto') // 加密模块 const xmlParser = require('koa-xml-body') //解析xml数据 app.use(xmlParser()) const router = new Router() app.use(static(__dirname + '/')) // 验证 router.get('/wechat', ctx => { console.log('校验url', ctx.url) const {query} = url.parse(ctx.url, true) const { signature, // 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 timestamp, // 时间戳 nonce, // 随机数 echostr // 随机字符串 } = query console.log('wechat', query) // 将 token timestamp nonce 三个参数进行字典序排序并用sha1加密 let str = [conf.token, timestamp, nonce].sort().join(''); console.log('str',str) let strSha1 = crypto.createHash('sha1').update(str).digest('hex'); console.log(`自己加密后的字符串为:${strSha1}`); console.log(`微信传入的加密字符为:${signature}`); console.log(`两者比较结果为:${signature == strSha1}`); // 签名对比,相同则按照微信要求返回echostr参数值 if (signature == strSha1) { ctx.body = echostr } else { ctx.body = "你不是微信" } } ) // 接受信息 router.post('/wechat', ctx => { const {xml: msg} = ctx.request.body console.log('Receive:', msg) const builder = new xml2js.Builder() const result = builder.buildObject({ xml: { ToUserName: msg.FromUserName, FromUserName: msg.ToUserName, CreateTime: Date.now(), MsgType: msg.MsgType, Content: 'Hello ' + msg.Content } }) ctx.body = result }) app.use(router.routes()); app.use(router.allowedMethods()); app.listen(3000);
接受消息
首先是微信向服务器发送get请求。
微信发出GET请求通常包括4个常见字段。
参数 |
描述 |
---|---|
signature |
加密签名,包括token、timestamp和nonce加密混成 |
timestamp |
时间戳 |
nonce |
随机数n+once |
echostr |
随机字符串 |
而微信发送消息,请求除了带上token、timestamp和nonce,还会带上一个xml数据包。
- 对token/timestamp/nonce进行字典排序
- 对排序完之后的字段拼接,sha1加密
- 以加密结果对比signature,二者相等则通过校验
发送消息
- 消息解析为字符串,获取XML数据(接收方,发送方,内容等)。
- 构造发回的xml数据包(注意原来的发送方变为接收方)
- 通过片echo把构造好的数据发出去!
SHA1算法
安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准 (Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息, SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。SHA1有如下特性: 不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要,(但会有1x10 ^ 48分之一的机 率出现相同的消息摘要,一般使用时忽略)。
哈希: 不可变长 -> 摘要固定长度
- 摘要
- 雪崩效应:密文变化后看不出来。
- 类似MD5 SHA256
官方api调用
实际上微信不仅是收发消息那么简单。
官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183
为了简化操作,你可以调用一个库,co-wechat的好基友—— co-wechat-api
https://github.com/node-webot/co-wechat-api
!image-20190804010154636
acess_token
accesstoken是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用accesstoken。开发者需要进行妥善保存。accesstoken的存储至少要保留512个字符空间。accesstoken的有效期目前为2个小时(7200s),需定时刷新,重复获取将导致上次获取的access_token失效。
公众平台的API调用所需的access_token的使用及生成方式说明:
1、建议公众号开发者使用中控服务器统一获取和刷新accesstoken,其他业务逻辑服务器所使用的accesstoken均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务;
2、目前accesstoken的有效期通过返回的expirein来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新accesstoken。在刷新过程中,中控服务器可对外继续输出的老accesstoken,此时公众平台后台会保证在5分钟内,新老access_token都可用,这保证了第三方业务的平滑过渡;
3、accesstoken的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新accesstoken的接口,这样便于业务服务器在API调用获知accesstoken已超时的情况下,可以触发accesstoken的刷新流程。
公众号和小程序均可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在“微信公众平台-开发-基本配置”页中获得(需要已经成为开发者,且帐号没有异常状态)。调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,点击查看设置方法,否则将无法调用成功。小程序无需配置IP白名单。
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
需要在后端引入axios,获取token则应该这么写:
router.get('/getTokens',async (ctx)=>{ const APPID=config.appid; const APPSECRET=config.appsecret; const api=`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${APPSECRET}` const res = await axios.get(api); console.log(res); Object.assign(tokenCache, res.data, { updateTime: Date.now() }); ctx.body = res.data })
前端调用这个接口,得到:
因为我经常要用,所以要写一个获取方法:
const getTokens=async function(){ if(!tokenCache.access_token||Date.now()-7200*1000>tokenCache.updateTime){ // console.log(222,Date.now()-7200*1000,tokenCache.updateTime) const APPID = config.appid; const APPSECRET = config.appsecret; const api = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${APPSECRET}` const res = await axios.get(api); return Object.assign(tokenCache, res.data, { updateTime: Date.now() }) }else{ return tokenCache; } }
实现关注用户列表
// 获取关注者列表 router.get('/getFollowers', async ctx => { await getTokens(); const api = `https://api.weixin.qq.com/cgi-bin/user/get?access_token=${tokenCache.access_token}` const res = await axios.get(api) ctx.body = res.data })
api接口于api调用,是微信验证我们的服务器。
Co-wechat工具库下更多api的调用
我想在index.html实现以下功能:
- 实际工作中,通常是用库来实现的。比如用户:
const WechatAPI = require('co-wechat-api'); const api = new WechatAPI(conf.appid, conf.appsecret); router.get('/getFollowers', async ctx => { let res = await api.getFollowers(); // 批量获取用户信息 let _res=await api.batchGetUsers(res.data.openid); ctx.body = _res })
全局票据的管理
按照上文的api,token是放到服务器运行内存里的。
获取accesstoken的次数是2000次。每次都调用时不现实的。而且在负载均衡情况下,accesstoken是放node1还是node2呢?
答案是放数据库里。
以mongodb为例:
// mongoose.js // 连接数据库: const mongoose = require('mongoose') const { Schema } = mongoose mongoose.connect('mongodb://localhost:27017/weixin', { useNewUrlParser: true }, () => { console.log('Mongodb connected..') }) exports.ServerToken = mongoose.model('ServerToken', { accessToken: String });
// index.js const {ServerToken}=require('./mongoose') const api = new WechatAPI(config.appid, config.appsecret, async function () { return await ServerToken.findOne() }, async function (token) { const res = await ServerToken.updateOne({}, token, { upsert: true }) //允许覆盖 } )
在new出 WechatAPI
实例的时候,实际上提供了第三第四个参数。第一个用于存token,第四个用于存放。
案例:微信在线投票
后端
先装依赖:
npm i koa koa-router koa-static koa-socket co-wechat -s
// index.js const Koa = require('koa') const Router = require('koa-router') const app = new Koa() const router = new Router() // 静态文件 const static = require('koa-static') app.use(static(__dirname + '/')) const conf = require('./conf') const wechat = require('co-wechat') // socket.io const IO = require('koa-socket') const io = new IO() io.attach(app) app._io.on('connection',socket => { console.log('socket connection..') }) // 消息接口 router.all('/wechat', wechat(conf).middleware( async (message, ctx) => { console.log('wechart', message) app._io.emit('chat',message) return '收到!'; } )) app.use(router.routes()); /*启动路由*/ app.use(router.allowedMethods()); app.listen(3000);
通过socket.io监听收发消息。
比较简单没什么可说的。
前端
前端通过vue和echarts展现数据
<div id="app"> <div id="chart" style="width:100%;height:50%;"></div> <cube-button @click="reset">重置</cube-button> <div class="view-wrapper"> <div class="list" v-for="item in list.slice(0,5)"> <div class="item"> <div class="avatar"></div> <div class="bubble"> <p>{{ item.Content }}</p> </div> </div> </div> </div> </div>
var app = new Vue({ el: "#app", data: { list: [] }, watch: { list: { handler(newName) { this.renderChart(); }, immediate: true, deep: true } }, methods: { initChart() { const option = { series: [{ type: "pie", selectedMode: "single", radius: [0, "70%"], label: { normal: { position: "inner" } }, labelLine: { normal: { show: false } } }] }; this.chart = echarts.init(document.getElementById("chart")); this.chart.setOption(option); }, // 刷新图表 renderChart(newName) { const data = ["1", "2"] .map(key => ({ name: key, value: this.list.filter(v => v.Content === key).length })) .filter(v => v.value !== 0); const option = { series: [{ data }] }; this.chart ? this.chart.setOption(option) : ""; }, // 重置 reset() { this.list = []; } }, mounted() { this.initChart(); const socket = io() socket.on('chat', msg => { console.log('chart ...', msg) this.list.unshift(msg) }) } });
效果如下
张家口公众号哪家好- 上一篇:张家口微信公众号广告哪家好
- 下一篇:张家口公众号制作报价