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

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

首先想到的是,需要客户去买服务器和域名,进行域名备案,然后为客户开发一个网站,能够通过公众平台的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);
      }
});

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

然后我们编写一段代码获取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的文档显示个人小程序不能使用,但实际上是可以使用的。https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html

参考

云开放的官方文档https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/console.html

 

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

palhotel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值