乐趣区

关于javascript:h5微信分享实现nodeexpress

前言

(^o^)/~
h5 页面在微信中应用微信分享性能,实现个性化分享模版。
从默认款式:

到自定义款式:

第一步:注册微信公众平台账号(已有略过此步)

https://mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN&token=

集体只能注册订阅号类型,服务号就须要企业认证了。企业权限大一些,能用的接口比拟全,分享的话集体的就行。

目前网页端能应用的微信性能(领取除外)都属于公众号的领域,隶属于微信公众平台,和微信开放平台无关;

注册完登录后能够查看本人的权限,能用那些性能。

第二步:接入 sdk

这一步容易卡关,应为首先须要一台能用的服务器和备案的域名;没方法,微信对接要求很刻薄,本地无奈实现对接
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#1

平安域名这里要求将某个密钥文件放在服务器根目录。

上面就要开始写代码了。

第三步:创立服务,搁置指定文件(有现成服务的放下文件,这一步跳过)

文件构造如下:

//server.js
const express = require('express');
const {resolve} = require('path');
const publicPath = resolve('./public');

const server = express();

async function bootstrap(){server.use(express.static(publicPath));// 托管动态文件
    server.listen(9222,function(){console.log('server run at:http://localhost:9222')
    })
}
bootstrap();

启动服务试一下:

查看文件是否失常查看:
http://localhost:9222/MP_verify_kuKy0AHjAhdrFeBO.txt

这样就能够放到服务器下来了。

个别用这两个工具操作服务器就够了。

将动态资源传上去,node_modules 手动装置比拟好:

假设服务器上曾经装置好了 node,npm,yarn.pm2 等工具。
我这里应用 yarn 装置,用 npm 也是一样的。

用 pm2 托管服务:
输出命令 pm2 start server.js

服务启动胜利就能够试一下 MP_verify_kuKy0AHjAhdrFeBO.txt 是否拜访。
http://www.liubingyang.top:9222/MP_verify_kuKy0AHjAhdrFeBO.txt
提醒:我的是 ubuntu 零碎,默认 9222 端口不可拜访,需关上防火墙限度:

ufw allow xxx// 凋谢接口
ufw status  查看接口放开状态


尽管能够拜访,但要求是间接拜访,不能带端口号,用 nginx 简略配置下就行:(默认曾经装置了 nginx)
找到 nginx 配置文件夹 nginx.conf(每个零碎不统一,应用 whereis nginx 能够粗略查找)

我的是在 /etc/nginx 下,进入 nginx.conf 文件夹下用 vim 间接创立编辑一个 test.conf 文件。


只须要如下代码就够了,那两个 add_header 是用来解决接口跨域申请的,也能够不要。

server {
        listen 80;
        server_name www.liubingyang.top;

        location / {
            add_header 'Access-Control-Allow-Origin' '$http_origin';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
            proxy_pass http://127.0.0.1:9222;
        }
}

在线编辑,或者本地编辑完用 xftp 传上去也能够。
编辑完保留后(编辑输出 i 命令,编辑完输出 ESC->:wq-> 回车),开启(或重启 nginx)

ngixn -s reload


失常(在 linux 零碎中,没有音讯就是最好的音讯)
再试试:
http://www.liubingyang.top/MP_verify_kuKy0AHjAhdrFeBO.txt

文件搁置失常;

第三步:页面接入 sdk

看文档,页面引入 js,并配置初始项。
新建 html

//inde.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    测试分享
</body>
</html>
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script>
   wx.config({
        debug: true, // 开启调试模式, 调用的所有 api 的返回值会在客户端 alert 进去,若要查看传入的参数,能够在 pc 端关上,参数信息会通过 log 打出,仅在 pc 端时才会打印。appId: '', // 必填,公众号的惟一标识
        timestamp: '', // 必填,生成签名的工夫戳
        nonceStr: '', // 必填,生成签名的随机串
        signature: '',// 必填,签名
        jsApiList: [] // 必填,须要应用的 JS 接口列表});
</script>

前端做的事件就是把 appId,timestamp,nonceStr,signature 这 4 个参数填下就行了,都是必填项。
appId在平台的根本配置中找到(页面拉到最上面);
https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=1496518416&lang=zh_CN

jsApiList 不能为空,就先把分享无关的几个接口先写进去:

jsApiList: ['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareQZone'] // 必填,须要应用的 JS 接口列表

timestamp,nonceStr,signature 三个参数由服务端对接提供(appId 也能够由服务端提供)。
接下来就开始做服务端对接,解决这三个参数的获取。

第四步:服务端获取 signature

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#1


依据文档的附录(下面这个页面往下翻)形容,能够判断出咱们须要的三个参数形成:
timestamp:工夫戳(特地提醒:就是个随机数字,new Date().getTime()能够获取,然而 12 位的,这里只有 10 位 ,截取一下就行了);
nonceStr:随机码,也是自定义的,这里写死也行。
signature:最重要的签名字段,是由下面两个加上 url 和 jsapi_ticket 拼成的字符串,再进行 sha1 加密失去。
例如:

jsapi_ticket=xx&noncestr=Wm3WZYTPz0wzccnW&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value

加密后会失去一个简短的密文:

上面就看怎么获取 jsapi_ticket:

分两步,先获取 access_token:
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html

介绍有趣味缓缓看吧,我只管接口,参数,返回就行了。
grant_type 固定值 client_credential,secret 和 appid 在一个中央查看。

新建 controller 文件夹和 config 文件夹,新增获取 access_token 的 controller 和 config 文件:

 //config/index.js
 module.exports = {
    grant_type:'client_credential',
    appid:'wx9c48ee416a064a99',// 换成本人的,和域名绑定的
    secret:'030fa057e50a2e3eedd42cc2e605edb6',// 换成本人的,和域名绑定的

    timestamp:new Date().getTime().toString().slice(0,10),
    nonceStr:'Wm3WZYTPz0wzccnW',

    localPath:'http://www.liubingyang.top',
}

//access_token.js
const {Router} =require("express");
const axios = require('axios');
const config = require('../config');
class Axios {async init(){const router =  Router();
        router.get('/',this.get);
        return router;
    }
    get = async (req,res)=>{let url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${config.appid}&secret=${config.secret}`;
        const {data} = await axios.get(url);
        res.json(data);
    }
}

module.exports = async function(){return await new Axios().init();}

controller 文件入口:

//contriller/index.js
const {Router} = require('express');
const access_tokenController = require('./access_token.js');

module.exports = async function(){const router = Router();
    router.use('/access_token', await access_tokenController());
    return router;
}

server.js 同样做下批改:

//server.js
const express = require('express');
const {resolve} = require('path');
const bodyParser= require('body-parser');
const server = express();

const publucPath = resolve('./public');

const initControllers = require('./controller');
const bootstrap = async function(){server.use(bodyParser.urlencoded({ extended: false}));// 解决表单入参
    server.use(bodyParser.json({ extended: false}));// 解决 json 入参
    server.use(express.static(publucPath));
    server.use(await initControllers());
    server.listen(9222,function(){console.log('server run at http://localhost:9222');
    })
}
bootstrap();

将批改后的代码放到服务器上:

装置 axios 插件,重启 pm2:

在浏览器中试一下接口
http://www.liubingyang.top/access_token

能够失常返回,上面用 access_token 换取 jsapi_ticket:

用 get 办法申请这个接口就能够了。新建 jsapi_ticket 的 controller:

//jsapi_ticket.js
const {Router} = require('express');
const axios = require('axios');
const urlUtil = require("url");
const querystring = require("querystring");

class Jsapi_ticket {async init(){const router = Router();
        router.use('/',this.get);
        return router;
    }
    get = async (req,res)=>{
        // 获取返回的 url 对象的 query 属性值 
        var arg = urlUtil.parse(req.url).query;

        // 将 arg 参数字符串反序列化为一个对象
        var params = querystring.parse(arg);
        const url = `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${params.access_token}&type=jsapi`;
        const {data} = await axios.get(url);
        res.json(data);
    }
}

module.exports = async function(){return await new Jsapi_ticket().init();}

controller/index.js 别忘注册下接口:

文件放到服务器上,装置 url 插件,重启 pm2:

启动胜利能够用 pm2 logs 查看日志,pm2 flush 能够清空所有日志。
试一下 Jsapi_ticket 接口:(access_token 有时效性,得用本人刚获取的)
[http://www.liubingyang.top/js…

这里也正确的拿到了 ticket。
接下来开始组装签名 signature:

//signature.js
const {Router} = require('express');
const axios = require('axios'); 
const config = require('../config');
const urlUtil = require("url");
const querystring = require("querystring");

class Signature{async init(){const router = Router();
        router.get('/',this.get)
        return router;
    }
    get = async (req,res)=>{
        // 获取返回的 url 对象的 query 属性值 
        var arg = urlUtil.parse(req.url).query;
        console.log('arg:'+arg);
        // 将 arg 参数字符串反序列化为一个对象
        var params = querystring.parse(arg);
        let access_token ='';
        {const { data} = await axios.get(`${config.localPath}/access_token`);
            if(data.access_token){access_token = data.access_token;}else{res.json(data);
                return;
            }
            console.log('access_token:'+access_token);
        }
        {const { data} = await axios.get(`${config.localPath}/jsapi_ticket?access_token=${access_token}`);
            let ticket = '';
            if(data.ticket){ticket = data.ticket;}else{res.json(data);
                return
            }
            console.log('ticket:'+ticket);
            console.log('url:'+params.url)
            const signature = `jsapi_ticket=${ticket}&noncestr=${config.nonceStr}&timestamp=${config.timestamp}&url=${params.url}`;
            console.log('signature:'+signature);
            res.json({signature});

        }


    }
}

module.exports = async function(){return await new Signature().init();}

应为是在服务器上调试,多写几个 console 便于查看问题,
controller/index.js 注册接口

上传文件重启 pm2:

这个时候就开着 pm2 logs 查看下接口:
http://www.liubingyang.top/signature?url=http://www.liubingyang.top


日志也进去了,接口也失常。

最初一步 前端获取参数

最初就是提供前端开接口获前端所有须要的参数:

//wx_config.js
const {Router} = require('express');
const axios = require('axios');
const sha1 = require('node-sha1');
const config = require('../config');

class Wx_config {async init(){const router = Router();
        router.use('/',this.post);
        return router;
    }
    post = async (req,res)=>{console.log('req.body'+JSON.stringify(req.body))
        console.log('req.body.url:'+req.body.url);
        const {data} = await axios(`${config.localPath}/signature?url=${req.body.url}`);
        let signature = '';

        if(data.signature){signature = data.signature;}else{res.json(data);
            return;
        }
        res.json({
            timestamp : config.timestamp,
            nonceStr : config.nonceStr,
            signature:sha1(signature),
        })
    }
}

module.exports = async function(){return await new Wx_config().init();}

最初更新下 controller/index.js

批改下 index.html, 这里就不啰嗦了,把须要的都放进去:

//index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    测试分享
</body>
</html>
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script>
    const xhr = new XMLHttpRequest();
    function wx_config(){
        return new Promise(res =>{xhr.onreadystatechange = function(event){if(event.target.status == 200 && event.target.readyState == 4){res(xhr.responseText);
                }
            }
            xhr.open('post', 'http://www.liubingyang.top/wx_config');
            xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
            xhr.send(`url=${encodeURIComponent(location.href.split('#')[0])}`);
        })
    }
    let configs = '';
    (async function(){configs = await wx_config();
        console.log(configs)
        const {timestamp,nonceStr,signature} = JSON.parse(configs);
        let config = {
            debug: true, // 开启调试模式, 调用的所有 api 的返回值会在客户端 alert 进去,若要查看传入的参数,能够在 pc 端关上,参数信息会通过 log 打出,仅在 pc 端时才会打印。appId: 'wx9c48ee416a064a99',// 必填,公众号的惟一标识

            timestamp, // 必填,生成签名的工夫戳
            nonceStr, // 必填,生成签名的随机串
            signature,// 必填,签名
            jsApiList: ['chooseImage','onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareQZone'] // 必填,须要应用的 JS 接口列表
        }
        wx.config(config);

        wx.ready(function(){
            let config={ 
                title: '测试用例', // 分享题目
                desc: '你看这个行不行', // 分享形容
                link: location.href, // 分享链接,该链接域名或门路必须与当前页面对应的公众号 JS 平安域名统一
                type: 'link',// 分享类型,music、video 或 link,不填默认为 link,
                imgUrl:'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603186815698&di=7ec300630a404299c855c73a99773e17&imgtype=0&src=http%3A%2F%2Fcdn.duitang.com%2Fuploads%2Fitem%2F201411%2F04%2F20141104225457_f8mrM.thumb.700_0.jpeg',
                success: function () {alert('分享测试胜利')
                }
            }
                wx.onMenuShareTimeline(config)
                wx.onMenuShareAppMessage(config)
                wx.onMenuShareQQ(config)
                wx.onMenuShareQZone(config)
            })
        })()
</script>

文件更新到服务器上,装置 node-sha1 插件并重启 pm2:

记得先清空日志
查看页面状况
http://www.liubingyang.top/

开的是 debug 模式,各个参数都能看到。参数失常,就能够用手机微信测试了:

示意配置胜利,这里最容易呈现的谬误是 invalid signature,签名不正确,解决办法照着这个查就行了

调用分享给敌人:

这样就没有问题了。
出现的后果

总结

在微信的条条框框中摸索摸索某个性能的实现是比拟艰难的,尤其是老手,文档像说明书一样,简直没有逻辑可寻,而且有很多中央更新的太快,而手册又比拟老。
比方这两个接口,实际上集体是没有权限的,文档里并没有阐明。集体只能用比拟老的快废除的接口

下面有很多我踩过的坑就不一一阐明了,兴许你遇不到,兴许你遇到的我没遇到过,缓缓摸索吧。微信性能实现其实次要由服务端实现,前端做的很少,相熟对接流程会使前端同学对微信体系有更深的了解,出了问题也能从容的应答。

退出移动版