h5微信分享實現(node+express);

前言

(^o^)/~
h5頁面在微信中使用微信分享功能,實現個性化分享模版。
從默認樣式:
image.pnghtml

到自定義樣式:
image.png前端

第一步:註冊微信公衆平臺帳號(已有略過此步)

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

我的只能註冊訂閱號類型,服務號就須要企業認證了。企業權限大一些,能用的接口比較全,分享的話我的的就行。linux

目前網頁端能使用的微信功能(支付除外)都屬於公衆號的範疇,隸屬於微信公衆平臺,和微信開放平臺無關;ios

註冊完登陸後能夠查看本身的權限,能用那些功能。
image.pngnginx

第二步:接入sdk

這一步容易卡關,應爲首先須要一臺能用的服務器和備案的域名;沒辦法,微信對接要求很苛刻,本地沒法實現對接
https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#1express

image.png

安全域名這裏要求將某個密鑰文件放在服務器根目錄。
image.pngnpm

下面就要開始寫代碼了。json

第三步:建立服務,放置指定文件(有現成服務的放下文件,這一步跳過)

文件結構以下:
image.pngbootstrap

//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();

啓動服務試一下:
image.png

查看文件是否正常查看:
http://localhost:9222/MP_verify_kuKy0AHjAhdrFeBO.txt
image.png
這樣就能夠放到服務器上去了。
image.png
通常用這兩個工具操做服務器就夠了。
image.png
將靜態資源傳上去,node_modules手動安裝比較好:
image.png
假定服務器上已經安裝好了node,npm,yarn.pm2等工具。
我這裏使用yarn安裝,用npm也是同樣的。

用pm2託管服務:
輸入命令pm2 start server.js
image.png
服務啓動成功就能夠試一下MP_verify_kuKy0AHjAhdrFeBO.txt可否訪問。
http://www.liubingyang.top:9222/MP_verify_kuKy0AHjAhdrFeBO.txt
提示:個人是ubuntu系統,默認9222端口不可訪問,需打開防火牆限制:

ufw allow xxx//開放接口
ufw status  查看接口放開狀態

image.png

image.png
雖然能夠訪問,但要求是直接訪問,不能帶端口號,用nginx簡單配置下就行:(默認已經安裝了nginx)
找到nginx配置文件夾 nginx.conf(每一個系統不一致,使用whereis nginx能夠粗略查找)
image.png
個人是在 /etc/nginx下,進入nginx.conf文件夾下用vim直接建立編輯一個test.conf文件。
image.png
image.png
只須要以下代碼就夠了,那兩個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

image.png
正常(在linux系統中,沒有消息就是最好的消息)
再試試:
http://www.liubingyang.top/MP_verify_kuKy0AHjAhdrFeBO.txt
image.png
文件放置正常;

第三步:頁面接入sdk

看文檔,頁面引入js,並配置初始項。
新建html
image.png
image.png

//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
image.png

image.png

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

image.png
根據文檔的附錄(上面這個頁面往下翻)描述,能夠判斷出咱們須要的三個參數構成:
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

加密後會獲得一個簡短的密文:
image.png

下面就看怎麼獲取jsapi_ticket:
image.png
分兩步,先獲取access_token:
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
image.png
介紹有興趣慢慢看吧,我只管接口,參數,返回就好了。
grant_type固定值client_credential,secret和appid在一個地方查看。
image.png
新建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();

將修改後的代碼放到服務器上:
image.png

安裝axios插件,重啓pm2:
image.png
在瀏覽器中試一下接口
http://www.liubingyang.top/access_token
image.png
能夠正常返回,下面用access_token換取jsapi_ticket:
image.png
用get方法請求這個接口就能夠了。新建jsapi_ticket的controller:
image.png

//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別忘註冊下接口:
image.png
文件放到服務器上,安裝url插件,重啓pm2:
image.png
啓動成功能夠用pm2 logs查看日誌,pm2 flush能夠清空全部日誌。
試一下Jsapi_ticket接口:(access_token有時效性,得用本身剛獲取的)
[http://www.liubingyang.top/js...
image.png
這裏也正確的拿到了ticket。
接下來開始組裝簽名signature:
image.png

//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註冊接口
image.png

上傳文件重啓pm2:
image.png
這個時候就開着pm2 logs查看下接口:
http://www.liubingyang.top/signature?url=http://www.liubingyang.top
image.png
image.png
日誌也出來了,接口也正常。

最後一步 前端獲取參數

最後就是提供前端開接口獲前端全部須要的參數:
image.png

//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
image.png
修改下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:
image.png
記得先清空日誌
查看頁面狀況
http://www.liubingyang.top/
image.png
開的是debug模式,各個參數都能看到。參數正常,就能夠用手機微信測試了:
image.png
表示配置成功,這裏最容易出現的錯誤是invalid signature,簽名不正確,解決辦法照着這個查就好了
image.png
調用分享給朋友:
image.png
這樣就沒有問題了。
呈現的結果image.png

總結

在微信的條條框框中探索探索某個功能的實現是比較困難的,尤爲是新手,文檔像說明書同樣,幾乎沒有邏輯可尋,並且有不少地方更新的太快,而手冊又比較老。
好比這兩個接口,實際上我的是沒有權限的,文檔裏並無說明。我的只能用比較老的快廢棄的接口
image.png

上面有不少我踩過的坑就不一一說明了,也許你遇不到,也許你遇到的我沒遇到過,慢慢探索吧。微信功能實現其實主要由服務端完成,前端作的不多,熟悉對接流程會使前端同窗對微信體系有更深的理解,出了問題也能從容的應對。

相關文章
相關標籤/搜索