微信小程序云开发获取关联公众号的文章列表

优采云 发布时间: 2020-08-13 00:36

  最近收到一个需求是:小程序获取关联公众号的文章并且显示。

  首先想到的是,需要顾客去买服务器和域名,进行域名备案,然后为顾客开发一个网站,能够通过公众平台的appid与key来获取公众号的素材列表,最后开发一个插口给小程序使用。一般顾客选购服务器和域名就是个困局,劝退了很多人。

  但是我查看了陌陌小程序的官方文档,发现有一个云开发功能,其中有云数据库、云存储、云函数三种功能。类似于Serverless,我们不需要去买服务器和域名了,直接使用小程序的云开发功能能够做好多事情。我就想试试能不能用云开发功能做获取公众号文章的事情。

  云数据库是一个类似于Redis的Key-Value数据库,使用陌陌提供的一些查询API,能够进行链式操作,有点像.Net的Entity Framework。

  云存储主要是拿来做上传与下载文件使用,可以拿来做相册等应用。

  云函数是提供了一个Node.Js的运行环境,我们就能布署自己的代码,也才能使用npm配置引入其他的开源软件包。云函数只能通过陌陌小程序框架里特有的方法调用,并且返回结果。与我们订购服务器自建服务不同的是,我们不清楚究竟有几个实例在运行,每次执行不一定在同一个实例中,所以我们不太适宜做一些在显存中缓存数据、保持状态的工作,应当及时把数据存入云数据库。云开发的云函数的独到优势在于与陌陌登陆信令的无缝整合。当小程序端调用云函数时,云函数的传入参数中会被注入小程序端用户的 openid,开发者可以直接使用该 openid。

  在做这个功能之前,首先是新建小程序,将公众平台与小程序关联,公众平台个人订阅号也是可以的。

  然后在小程序的项目中,project.config.json文件中加入一个配置项,指定云函数的储存目录。

  

"cloudfunctionRoot": "clouds/"

  新建这个clouds目录,在其中构建一个文件夹,比如我们命名为fetch,以后调用云函数也用这个名称,然后在其中新建index.js,作为云函数的入口。一个云函数的基本结构是:

  

// 云函数入口文件

const cloud = require('wx-server-sdk');

const request = require('request');

cloud.init();

// 云函数入口函数

exports.main = async (event, context) => {

return {200: 40400, msg: 'success'};

}

  我们可以象MVC和Servlet等Web框架一样,要求客户端传一个action参数,然后按照不同的action参数,选择对应的处理函数,也可以创建多个云函数。代码类似于:

  

var action = event.action;

if(!action){

return {code: 20000, msg: 'hello, world'};

}

//dispatcher

var ret;

if(action === 'role'){

ret = await registerAndReturnUser(event);

} else if(action === 'userlist'){

ret = await fetchUserList(event);

} else if(action === 'updaterole'){

ret = await updateUser(event);

}

  当然了我们可以使用npm install --save安装任何的npm开源包,像往常一样构建package.json,每次更新了云函数,都应当在云函数文件夹上点击右键,选择“上传并布署(云端安装依赖)”。

  小程序后端调用云函数的代码是,无需晓得真实的URL,只需按云函数名称调用即可:

  

wx.cloud.callFunction({

// 云函数名称

name: 'fetch',

// 传给云函数的参数

data: {

keywords: this.data.keywords,

page: this.data.page,

limit: this.data.limit

},

success: function (res) {

console.log(res);

},

fail: err => {

console.log(err);

}

});

  获取公众号的永久素材列表,可以参考这个文档。需要一个AccessToken,而AccessToken的过期时间是2小时,每天限制申请的次数,腾讯也建议在第三方服务器保存这个AccessToken。我们就可以把这个AccessToken存入云数据库中。申请AccessToken的文档在。

  然后我们编撰一段代码获取AccessToken,然后查看云函数的日志,或者是我们主动加上throw句子,抛出异常,在小程序调试控制台也可以见到返回结果,经常会看见40164错误,这个错误会提示我们某个IP不在白名单中,我们就可以把这个IP加入微信公众号配置的白名单中。但是问题来了,小程序云开发的出口IP是浮动的,可能是一个集群在提供服务,不一定那个IP起作用。但是他又是有限的,当我们加了十几个IP时,发现常常可以命中我们的白名单列表,所以我们应当多试几次,把尽可能多的IP找下来。所以我就编撰了这样一段程序,当获取AccessToken失败,并且错误是40164时侯,首先在数据库中储存这个IP,等我们有时间了就把这种IP加入白名单,反复几次我们的白名单里就有一二十个IP了。这时候发觉我们的命中率上升了,请求好几次才有一次不命中的。所以我就编撰恳求失败之后立刻再恳求一次,最多恳求五次,类似于“再哈希”的算法,假设我们有50%的命中率,连续5次不命中的机率是3%左右,经过实验发觉,一般都能在五次之内成功获取AccessToken。

  这段代码大致如下:

  

var getWechatAccessToken = async function(counter) {

//先检查数据库,如果token过期,返回空,继续执行网络请求

var tokenInDb = await this.getAccessTokenFromDB();

if (tokenInDb) {

return tokenInDb['accessToken'];

}

var token_url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + this.appid + '&' + 'secret=' + this.secret;

var result = await rp({

url: token_url,

method: 'GET'

});

var obj = (typeof result.body === 'object') ? result.body : JSON.parse(result.body);

if (obj.errcode && obj.errcode === 40164) {

var ip = obj.errmsg.split(' ')[2];

//保存IP到数据库

await this.insertIp(ip);

//try again and save ip to database;

counter += 1;

if (counter < 5) {

return await this.getWechatAccessToken(counter);

}

}

//保存AccessToken到数据库

if (obj['access_token']) {

this.saveTokenToDb(obj['access_token']);

return obj['access_token'];

}

return null;

}

  我们储存了申请AccessToken的时间,可以按照这个时间选择使用这个数据库中储存的Token,或者是重新获取新的Token。接下来就是使用获取素材列表的插口获取所有的公众号文章。我们一直把它们存入数据库,这样上次小程序恳求,就可以直接从云数据库中访问。获取公众号素材列表有个限制是一次只能获取20条,我们须要先查出总量,然后分页获取。同样的是,查询云数据库一次最多获取100条,我们也是须要先查出总量,再分页查询所有的。

  

async fetchNewsFromServer(token, offset) {

var url = 'https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=' + token;

var body = {

"type": "news",

"offset": offset,

"count": 20

};

var result = await rp({

url: url,

method: 'POST',

headers: {

'content-type': 'application/json',

},

body: JSON.stringify(body)

});

return result;

}

//采集公众号的所有文章

async collectDataFromServer(token, dbcount) {

lastFetchTime = new Date();

var countUrl = 'https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=' + token;

var countResult = await rp({

url: countUrl,

method: 'GET',

});

var countObj = (typeof countResult.body === 'object') ? countResult.body : JSON.parse(countResult.body);

var total = 0;

if (countObj) {

total = countObj['news_count'];

if (total === dbcount) {

return;

}

} else {

return;

}

//每次获取20个

var fetchTimes = parseInt((total - 1) / 20) + 1;

var i;

var newsList = [];

for (i = 0; i < fetchTimes; i++) {

var offset = i * 20;

var newsResult = await this.fetchNewsFromServer(token, offset);

var news = (typeof newsResult.body === 'object') ? newsResult.body : JSON.parse(newsResult.body);

if (!news || !news.item_count) {

continue;

}

var j = 0;

var items = news.item;

for (j = 0; j < news.item_count; j++) {

newsList.push(items[j]);

}

}

//先清空原有的数据再存储新的

await this.clearTable('posts');

for (var k = 0; k < newsList.length; k++) {

var toSave = newsList[k];

var toSaveObj = {

mediaid: toSave.media_id,

url: toSave.content.news_item[0].url,

pic: toSave.content.news_item[0].thumb_url,

title: toSave.content.news_item[0].title

};

await db.collection('posts').add({

data: toSaveObj

});

}

}

  然后就是小程序客户端通过云函数获取数据,在后端展示了,我们可以获取文章的链接,在WebView中展示。WebView只能显示关联公众号的文章和小程序后台填写的域名的网址。WebView的文档显示个人小程序不能使用,但实际上是可以使用的。。

  参考

  云开放的官方文档

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线