乐趣区

关于node.js:记一次个人开发者如何让H5婚礼请帖在微信里体面的流转

我的公众号原文地址 📖

⚠️ 很要害的前提 ⚠️
❗️集体开发者应用 wx-jssdk 的权限受限❗️(无转发给好友、分享朋友圈等权限),可先去「设置与开发 - 接口权限」查看权限详情。
🤷‍♂️文末是我说的「体面」❗️

公众号设置

服务端代码

(基于 Koa2 开发,省略服务器搭建代码,仅提供关键步骤代码)

1. 编写「服务器配置 - 服务器地址(URL)」所须要的接口地址

https://yourserver.com/api/ch…

let sha1 = require("sha1");

/**
 * 微信公众号 - 指定服务器地址 - 验证接口
 * @param {*} ctx
 */
checkToken: async (ctx) => {const { signature, timestamp, nonce, echostr} = ctx.query;
  //ctx.query 获取申请中携带的参数
  let {token} = wxconfig;
  // 将 Token,timestamp,nonce 按字典排序, 排序后链接成一个字符串
  let str = [token, timestamp, nonce].sort().join("");
  // 应用 sha1 模块进行 sha1 加密
  let sha1Str = sha1(str);

  // 判断加密后的字符串与申请中 signature 是否相等
  if (sha1Str === signature) {ctx.body = echostr;} else {ctx.body = "Token check failed.";}
};

这一步 OK 之后,在「微信公众号后盾就能胜利开启服务器了」。接下来就是编写 H5 在微信里调用 wx-jssdk 相干性能所须要的受权过程。其实次要就是拿到 wxconfig 所须要的一些必要参数

编写 wx.config 所需数据的接口

https://yourserver.com/api/wx… 需受权网站的 url

生成 wxconfig 数据的大抵流程:获取 access_token,通过access_token 获取 jsapi_ticket;而后拼接access_tokenjsapi_ticketnoncestrtimestamp 要受权的页面的 url生成签名 signature,最终将appIdtimestampnonceStrsignature 返回给前端,前端调用 wx.config 进行注册,胜利后就能够在 wx.ready 中调用相干 js 接口了。

wxConfig.js 文件代码
!!!这里局部代码借鉴来的,懒得改了大抵流程都是这样!

const sha1 = require("sha1");
const request = require("request");
const {wxconfig} = require("./wxConfig");

// 为了应答缓存压力,不要每次刷新 token,访问量高会带来很大问题
// 因为获取 access_token 的接口,一天最多调用 2000 次,每次有效期是两个小时
let CACHE = {
  ticket: "",
  ticketTimeout: 0, //ticket 过期工夫
  ticketTime: 0, // 获取 ticket 工夫

  accessToken: "",
  accessTokenTimeout: 0, //token 过期工夫
  accessTokenTime: 0, // 获取 token 工夫
};

class wxModel {
  /**
   * 刷新 access_token
   */
  static async refreshAccessToken() {return new Promise((resolve, reject) => {const tokenUrl = `${wxconfig.getAccessTokenUrl}?grant_type=client_credential&appid=${wxconfig.appId}&secret=${wxconfig.appSecret}`;
      request(tokenUrl, (error, response, body) => {if (typeof body === "string") {
          try {body = JSON.parse(body);
          } catch (e) {
            body = {
              errcode: "-1000",
              body,
            };
          }
        }

        if (body && (!body.errcode || body.errcode == 0)) {
          CACHE.accessToken = body.access_token;
          CACHE.accessTokenTimeout = body.expires_in * 500;
          CACHE.accessTokenTime = new Date();
          resolve(CACHE.accessToken);
        } else if (body) {reject(body.errmsg);
        } else {reject("未知异样");
        }
      });
    });
  }

  /**
   * 刷新 ticket
   * @param {*} access_token
   * @param {*} callback
   */
  static async refreshJsapiTicket(access_token) {
    // Jsapi_ticket
    return new Promise((resolve, reject) => {let ticketUrl = `${wxconfig.getJsapiTicketUrl}?access_token=${access_token}&type=jsapi`;
      request(ticketUrl, function (err, response, content) {content = JSON.parse(content);
        if (content && (content.errcode == 0 || !content.errcode)) {
          CACHE.ticket = content.ticket;
          CACHE.ticketTimeout = content.expires_in * 500;
          CACHE.accessTokenTime = new Date();
          resolve(CACHE.ticket); // ticket
        } else if (content) {reject(content.errmsg);
        } else {reject("未知异样");
        }
      });
    });
  }

  /**
   * 获取 wxconfig
   * @param {*} url
   * @returns
   */
  static async geneWxConfig(url) {
    // 获取 access_token
    let access_token = CACHE.accessToken;
    let ticket = CACHE.ticket;

    if (
      !access_token ||
      new Date() - CACHE.accessTokenTime > CACHE.accessTokenTimeout) {access_token = await this.refreshAccessToken();
      ticket = await this.refreshJsapiTicket(access_token);
    }

    let nonceStr = this.createNonceStr();
    let timestamp = this.createTimestamp();
    let signature = this.createSign({
      jsapi_ticket: ticket,
      nonceStr,
      timestamp,
      url,
    });

    return {
      appId: wxconfig.appId,
      access_token,
      ticket,
      timestamp,
      nonceStr,
      signature,
    };
  }

  /**
   * 随机字符串
   */
  static createNonceStr() {return Math.random().toString(36).substr(2, 15);
  }
  /**
   * 工夫戳
   */
  static createTimestamp() {return parseInt(new Date().getTime() / 1000).toString();}

  /**
   * 生成签名
   * ⚠️ 只对 url# 后面局部加密
   * ⚠️ noncestr 全副小写
   * @param {*} config
   */
  static createSign(config) {
    let ret = {
      jsapi_ticket: config.jsapi_ticket,
      nonceStr: config.nonceStr,
      timestamp: config.timestamp,
      url: config.url,
    };
    let url = ret.url.split("#")[0];
    let string = `jsapi_ticket=${ret.jsapi_ticket}&noncestr=${ret.nonceStr}&timestamp=${ret.timestamp}&url=${url}`;
    let shaObjs = sha1(string);
    return shaObjs;
  }
}

module.exports = wxModel;

wxConfig.js 文件内容

const wxconfig = {
  appId: "wx9xxxxxxx",
  appSecret: "xxxxxxxxxxxxxxxxxxxxx",
  token: "hxxxt", // 公众号后盾自行配置的 token

  getAccessTokenUrl: "https://api.weixin.qq.com/cgi-bin/token",
  getJsapiTicketUrl: "https://api.weixin.qq.com/cgi-bin/ticket/getticket",
};

module.exports = {wxconfig,};

前端代码

1. 引入 wx-jssdk
let jssdkUri = `${window.location.protocol}//res.wx.qq.com/open/js/jweixin-1.6.0.js`;

loadJs() {return new Promise((resolve, reject) => {if (window.wx) {return resolve(window.wx);
    }

    let script = document.createElement("script");
    script.type = "text/javascript";
    script.src = this.jssdkUri;
    window.document.getElementsByTagName("head")[0].appendChild(script);

    script.onload = () => {resolve(window.wx);
    };
    script.onerror = reject;
  });
}
2. 申请 /api/wxconfig 接口,拿到必要数据后调用 wx.config
let url = window.location.href;
let data = await this.getWxconfig(url);

wx.config({
  debug: false, // 开启调试模式, 调用的所有 api 的返回值会在客户端 alert 进去,若要查看传入的参数,能够在 pc 端关上,参数信息会通过 log 打出,仅在 pc 端时才会打印。appId: data.appId, // 必填,公众号的惟一标识
  timestamp: data.timestamp, // 必填,生成签名的工夫戳
  nonceStr: data.nonceStr, // 必填,生成签名的随机串
  signature: data.signature, // 必填,签名
  jsApiList: [
    "hideMenuItems",
    "updateAppMessageShareData",
    "updateTimelineShareData",
  ], // 必填,须要应用的 JS 接口列表
});
3. 在 wx.ready 中编写「分享、转发」和其它业务须要的办法
share({title, desc, link, imgUrl}) {this.shareData = { title, desc, link, imgUrl};

  if (!this.isReady) {console.error("wx jssdk is not ready.");
    return;
  }
  wx.ready(() => {
    wx.updateAppMessageShareData({
      title,
      desc,
      link,
      imgUrl, // 分享图标
      success: (res) => {},
      fail: (err) => this.onError(err),
    });

    wx.updateTimelineShareData({
      title,
      link,
      imgUrl, // 分享图标
      success: (res) => {},
      fail: (err) => this.onError(err),
    });
  });
}

hideMenu() {if (!this.isReady) {console.error("wx jssdk is not ready.");
    return;
  }

  wx.ready(() => {
    wx.hideMenuItems({
      menuList: [
        "menuItem:share:appMessage",
        "menuItem:share:timeline",
        "menuItem:share:qq",
        "menuItem:share:weiboApp",
        "menuItem:share:QZone",
        "menuItem:copyUrl",
      ],
    });
  });
}
如何让 H5 在微信里活的「体面」!

最初想说一下,作为一个集体开发者无奈调用 wx.updateAppMessageShareDatawx.updateTimelineShareData等一些接口,我是怎么利用无限的性能让我的 H5 网页尽量「体面」的吧!

既然不能分享,那罗唆干掉微信点击右上角性能按钮后弹出来的「转发给敌人」和「分享到朋友圈」,甚至干掉「复制链接」性能菜单。

这样尽量减少转发进来只剩下袒露的难看的 url 链接的状况产生。

(请看比照图)

至于如何流传这个 H5 网页?给 H5 生成一个二维码,贴在一张难看的图片上,转发给敌人吧哈哈哈!

😕顺便附上一个 Amazing 的收费在线 二维码生成网站

退出移动版