• 微信JSSDK使用指南


        这篇文章主要来说说微信JSSDK的一些配置和微信分享的使用,包括从前端sdk文件引入到server端和微信server的交互。

    另外Tangide已经把一些工作做好了。我会简要说一说怎么把它用起来。在开发的过程中主要參考了微信官方开发文档,因为微信提供的功能略多,我会定期更新文档,假设你已经熟悉了微信JSSDK的开发流程能够直接跳过前面的步骤,直接跳转到Tangide与微信JSSDK的使用结合 :)。


    1. 首先得有个公众号

    微信把公众号细分为服务号订阅号

    接口权限各不同样,假设你记不住你的公众号有没有相应功能的权限,能够到开发人员中心->接口权限表相应查看。

    2. 配置JS接口安全域名

    微信限制了JS接口使用的域名。详细在:公众号设置->功能设置页面。好在能够同一时候配置3个,并且能够改动: js-api

    这意味着配置能够复用了。反复劳动降低了:)。

    3. 引入JS文件

    http://res.wx.qq.com/open/js/jweixin-1.0.0.js这个是微信官方提供的sdk。能够在h5页面放个<script>标签把它引入进来。

    4. 注入权限验证配置

    微信为了验证调用SDK的合法性,须要我们的server与微信server交互获取配置信息。

    wx.config({
        debug: true, // 开启调试模式,调用的全部api的返回值会在clientalert出来。若要查看传入的參数。能够在pc端打开,參数信息会通过log打出。仅在pc端时才会打印。
        appId: '', // 必填。公众号的唯一标识
        timestamp: , // 必填。生成签名的时间戳
        nonceStr: '', // 必填,生成签名的随机串
        signature: '',// 必填。签名,见附录1
        jsApiList: [] // 必填,须要使用的JS接口列表,全部JS接口列表见附录2
    });

    一般来说仅仅有是否开启调式模式由client决定,其它的都是通过http请求server获得,以下我们就来看看怎样从服务获取配置信息。

    5. 服务端获取配置信息

    这是整个过程相对关键的一步,你必须要缓存好两个token: jsapi_ticket以及access_tokenaccess_token有效时间为2个小时。向微信server请求频率限制为:每天2000次。微信还有其它一些參数的频率限制,你能够看这里。


    以下来说说获取配置信息的详细步骤:

    get config uml

    来看看server端是怎么处理的:

    nodejsserver端:

    var fs = require('fs');
    var path = require('path');
    var urlParser = require('url');
    var TokenManager = require('./token_manager');
    
    var Handler = function() {
    };
    
    Handler.doit = function(req, res) {
        try {
            var query = urlParser.parse(req.url, true).query;
            if(!query || !query.url) {
                throw new Error('invalid params');
            }
            var realUrl= atob(decodeURI(query.url));
        }
        catch(e) {
            res.status(401).json({code: -1, message: e});
            res.end();
            return;
        }
        Handler.tokens.getJsApiTicket(realUrl, function(err, config) {
            if(!!err) {
               res.status(500).json({code: -1, message: err});
            }
            else {
               res.status(200).json({code: 0, message: 'success', data: config});
            }
            res.end();
        });
    };
    
    Handler.init = function(app, express, config) {
        Handler.opts = {
            appid: config.appid,
            secret:config.secret
        };
        Handler.tokens = TokenManager.create(opts);
        app.post('/weixin/get-config.php', Handler.doit);
    };
    
    module.exports = Handler;
    
    //////////////////////////////////////////////////////////////////////////////
    //token manager
    
    var TokenManager = function(opts) {
        this.opts = opts;
        if(!opts.appid || !opts.secret) {
            thow new Error('appid and secret should not be empty!!!');
        }
        var tokenPath = path.join(__dirname, 'tokens.json');
        this.loadToken = opts.loadToken || function(type, cb) {
            fs.readFile(tokenPath, 'utf8', function(err, data) {
                if(!!err) {
                    return cb(err);
                }
                try {
                    data = data || JSON.stringify({});
                    data = JSON.parse(data);
                }
                catch(e) {
                   return cb(e);
                }
                cb(null, data[type] || {});
            });
        };
        this.saveToken = opts.saveToken || function(token, cb) {
            fs.readFile(tokenPath, 'utf8', function(err, data) {        
                if(!!err) {
                    return cb(err);
                }
                data = data || {};
                var type = Object.keys(token)[0];
                data[type] = token[type];
                fs.writeFile(tokenPath, JSON.stringify(data), cb);
            });
        };
    };
    
    var isValid = function() {
        return !!this.token && (new Date().getTime()) < this.expireTime;
    };
    
    var load = function(stream, cb) {
        var chunks = [];
        stream.on('data', function(chunk) {
            chunks.push(chunk);
        });
        stream.on('error', function(err) {
            cb(err);
        });
        stream.on('end', function() {
            cb(null, Buffer.concat(chunks));
        });
    };
    
    var request = function(url, cb) {
        require('https').get(url, function(res) {
            load(res, cb);
        }).on('error', function(err) {
            cb(err);
        });
    };
    
    var wrapper = function(cb) {
        return function(err, rawData) {
             cb = cb || function() {};
             if(!!err) {
                 return cb(err);
             }
             try {
                 var data = rawData.toString('utf-8');
                 data = JSON.parse(data);
             }
             catch(err) {
                  return cb(err);
             }
             if(data && data.errcode) {
                  err = new Error(data.errmsg);
                  err.name = 'WeChatAPIError';
                  err.code = data.errcode;
                  return cb(err, data);
             }
             cb(null, data);
        };
    };
    
    TokenManager.prototype.getAccessToken = function(cb) {
        var self = this;
        this.loadToken('accessToken', function(err, token) {
             if(err) {
                cb(err);
             }
             else if(isValid.call(token)) {
                cb(null, token.token);
             }
             else {
                  var url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential' +
                            this.opts.appid + this.opts.secret;
                  request(url, wrapper(function(err, data) {
                      if(err) {
                          return cb(err);
                      }
                      console.assert(data && data.token);
                      var expireTime = (new Date().getTime()) + (data.expires_in - 10) * 1000;
                      var token = {
                          accessToken: {
                              'token': data.token,
                              'expireTime': expireTime
                          }
                      };
                      self.saveToken(token, function(err) {
                           if(err) {
                               return cb(err);
                           }
                           cb(null, data.token);
                      });
                  });
             }
        });
    };
    
    TokenManager.prototype.getJsApiTicket = function(cb) {
        var self = this;
        this.loadToken('jsApiTicket', function(err, token) {
             if(err) {
                cb(err);
             }
             else if(isValid.call(token)) {
                cb(null, token.token);
             }
             else {
                  self.getAccessToken(function(err, token) {
                  if(err) {
                      return cb(err);
                  }
                  console.assert(token);
                  var url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?

    type=jsapi&access_token=' + token; request(url, wrapper(function(err, data) { if(err) { return cb(err); } console.assert(data && data.token); var expireTime = (new Date().getTime()) + (data.expires_in - 10) * 1000; var token = { jsApiTicket: { 'token': data.ticket, 'expireTime': expireTime } }; self.saveToken(token, function(err) { if(err) { return cb(err); } cb(null, data.token); }); }); } }); }; var createNonceStr = function(length) { return Math.random().toString(36).substr(2, length || 15); }; var createTimestamp = function() { return parseInt(new Date().getTime()/1000, 10) + ''; }; var crypto = require('crypto'); var genSignature = function() { var raw = function() { var keys = Object.keys(arguments); var newArgs = {}; keys.sort().forEach(function(key) { newArgs[key.toLowerCase()] = arguments[key]; }); var str = ''; for(var k in newArgs) { str += '&' + k + '=' + newArgs[k]; } return str.substr(1); }; var str = raw.apply(null, arguments); return crypto.createHash('sha1').update(str).digest('hex'); }; TokenManager.prototype.getJSSDKConfig = function(url, cb) { var self = this; this.getJsApiTicket(function(err, ticket) { if(err) { return cb(err); } console.assert(ticket); var nonceStr = createNonceStr(); var timestamp= createTimestamp(); var signature= genSignature(nonceStr, timestamp, ticket, url); var ret = { appId: self.opts.appid, nonceStr: nonceStr, timestamp: timestamp, signature: signature, url: url, jsApiList: [ 'checkJsApi', 'chooseImage', 'previewImage', 'uploadImage', 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo' ] }; cb(null, ret); }); }; module.exports.create = function(opts) { if(!this.inst) { this.inst = new TokenManager(opts); } return inst; };

        代码比較长,将就看吧!假设看过微信开发文档中的关于授权的大致流程,以上代码就没有什么问题了。须要注意的是,默认token保存在本地文件,假设有其它cluster需求能够自行注入函数来处理。

    phpserver端

    <?php
    class JSSDK {
      private $appId;
      private $appSecret;
      public function __construct($appId, $appSecret) {
        $this->appId = $appId;
        $this->appSecret = $appSecret;
      }
      public function getSignPackage($url) {
        $jsapiTicket = $this->getJsApiTicket();
        if(!$url) {
            $url = "http://$_SERVER[HTTP_HOST]" . '/weixin/demo/index.html';
        }
        $timestamp = time();
        $nonceStr = $this->createNonceStr();
        // 这里參数的顺序要依照 key 值 ASCII 码升序排序
        $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
        $signature = sha1($string);
        $signPackage = array(
          "appId"     => $this->appId,
          "nonceStr"  => $nonceStr,
          "timestamp" => $timestamp,
          "url"       => $url,
          "signature" => $signature,
          "rawString" => $string
        );
        return $signPackage; 
      }
      private function createNonceStr($length = 16) {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
          $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
      }
    
      private function getFileName($name) {
        //$fileName = sys_get_temp_dir() . "/weixin-" . $name;
        $fileName = dirname(__FILE__) . "/weixin-". $name;
        return $fileName;
      }
    
      private function saveData($name, $content) {
        $fileName = $this->getFileName($name);
    
        $fp = fopen($fileName, "wb+");
        if($fp) {
            fwrite($fp, $content);
            fclose($fp);
        }
    
        return;
      }
    
      private function loadData($name, $defaultContent) {
        $fileName = $this->getFileName($name);
        $content = file_get_contents($fileName);
    
        if($content) {
            return $content;
        }
        else {
            return $defaultContent;
        }
    
        return;
      }
    
      private function getJsApiTicket() {
        $data = json_decode($this->loadData("jsapi_ticket.json", '{"jsapi_ticket":"", "expire_time":0}'));
        if ($data->expire_time < time()) {
          $accessToken = $this->getAccessToken();
          $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken";
          $res = json_decode($this->httpGet($url));
          $ticket = $res->ticket;
          if ($ticket) {
            $data->expire_time = time() + 3600;
            $data->jsapi_ticket = $ticket;
            $this->saveData("jsapi_ticket.json",  json_encode($data));
          }
        } else {
          $ticket = $data->jsapi_ticket;
        }
        return $ticket;
      }
    
      private function getAccessToken() {
        $data = json_decode($this->loadData("access_token.json", '{"access_token":"", "expire_time": 0}'));
        if ($data->expire_time < time()) {
          $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
          $res = json_decode($this->httpGet($url));
          if(!isset($res->access_token)) {
            print_r($res);
            exit;
          }
    
          $access_token = $res->access_token;
          if ($access_token) {
            $data->expire_time = time() + 3000;
            $data->access_token = $access_token;
            $this->saveData("access_token.json", json_encode($data));
          }
        } else {
          $access_token = $data->access_token;
        }
        return $access_token;
      }
    
      private function httpGet($url) {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_URL, $url);
        $res = curl_exec($curl);
        curl_close($curl);
        return $res;
      }
    }
    
    session_start();
    if(!isset($_REQUEST['url'])) {
        echo "invalid arguments";
        exit;
    }
    
    $appID = "你的公众号appid";
    $appSecret = "你的公众号secret";
    
    $url = $_REQUEST['url'];
    $url = base64_decode($url);
    
    $jssdk = new JSSDK($appID, $appSecret);
    $signPackage = $jssdk->getSignPackage($url); 
    $signPackage['jsApiList'] = array('checkJsApi', 'chooseImage', 
                'onMenuShareTimeline', 'onMenuShareAppMessage', 
                'onMenuShareQQ', 'previewImage', 'uploadImage', 
                'downloadImage', 'getNetworkType');
    echo json_encode($signPackage);
    ?

    >

    php版本号流程都是一样。仅仅是语言差异罢了。

    6. 写个demo来验证一下

    因为微信团队限制没有提供主动分享的接口,我们仅仅能准备好要分享的数据。等到用户点击右上角的分享button的时候,才把我们之前准备好的数据分享出去。

    demo包括一个能够动态设置文本的input标签,你能够动态改动其内容,然后点击微信右上角的分享给朋友查看效果。

    <!DOCTYPE HTML>
    <html>
    <head>
        <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
        <meta http-equiv="pragma" content="no-cache"/>
        <meta http-equiv="Expires" content="0" />
        <meta http-equiv="Content-Type" content="type=text/html; charset=utf-8"/>
        <title>wechat-jssdk demo</title>
        <style>
        body {
            text-align: center;
        }
        input {
            width: 200px;
            height: 80px;
        font-size:40px;
        }
        </style>
        <script type="text/javascript" src = "http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
        <script type="text/javascript">
            function get(uri) {
                return http(uri, 'GET', null);  
            }
        function http(uri, method, data, headers) {
            alert('promise: ' + typeof Promise);
            return new Promise(function(resolve, reject) {
                var xhr = new XMLHttpRequest();
            xhr.open(method, uri, true);
            if(headers) {
            for(var p in headers) {
                xhr.setRequestHeader(p, headers[p]);
            }
            }
            xhr.addEventListener('readystatechange', function(e) {
            if(xhr.readyState === 4) {
                if(String(xhr.status).match(/^2dd$/)) {
                try {
                    var data = JSON.parse(xhr.responseText);
                    resolve(data);
                }
                catch(e) {
                    reject(e);  
                }
                }
                else {
                reject(xhr);
                }
            }
            });
            xhr.send(data);
        }); 
        }
    
            function wechatShare(cfg) {
            var inter = setInterval(function() {
            try {
                wx.config(cfg);
                clearInterval(inter);
            }
            catch(e) {
                console.log('wait ready');  
            }
            }, 200);
    
            var titleItem = document.getElementById('shareTitle');
            titleItem.onchange = function() {
            var shareTitle = titleItem.value;
            alert('shareTitle: ' + shareTitle);
            wx.onMenuShareAppMessage({
                title: shareTitle, // 分享标题
                desc: 'TangIDE test onMenuShareAppMessage', // 分享描写叙述
                link: window.location.href, // 分享链接
                imgUrl: 'http://img1.bdstatic.com/static/common/widget/search_box_home/logo/home_white_logo_0ddf152.png',
                type: 'link', // 分享类型,music、video或link,不填默觉得link
                success: function () { 
                // 用户确认分享后运行的回调函数
                },
                cancel: function () { 
                // 用户取消分享后运行的回调函数
                }
               });
            };
    
            wx.ready(function() {
            /*
                wx.onMenuShareAppMessage({
                title: '1234567', // 分享标题
                desc: 'TangIDE test onMenuShareAppMessage', // 分享描写叙述
                link: window.location.href, // 分享链接
                imgUrl: 'http://img1.bdstatic.com/static/common/widget/search_box_home/logo/home_white_logo_0ddf152.png',
                type: 'link', // 分享类型,music、video或link,不填默觉得link
                success: function () { 
                    // 用户确认分享后运行的回调函数
                },
                cancel: function () { 
                    // 用户取消分享后运行的回调函数
                }
                });
            */
            });
    
            wx.error(function(res) {
    
            });
            }
    
        function regConfig() {
            var uri = 'http://www.i5r.com.cn/weixin/php/json_config.php' +
                    '?url=' + window.btoa(location.href);
            get(uri).then(function(data) {
                data.debug = true;
            wechatShare(data);
            }).catch(function(err) {
                alert('request error:' + err);  
            });
        }
    
        window.onload = function() {
            setTimeout(regConfig, 600);
        };
        </script>
    </head>
    <body>
        <p><input placeholder="分享标题" type="text" id="shareTitle" required="required"/></p>
    </body>
    </html>

        config信息验证后会运行ready方法。全部接口调用都必须在config接口获得结果之后,config是一个client的异步操作。所以假设须要在页面载入时就调用相关接口。则须把相关接口放在ready函数中调用来确保正确运行。对于用户触发时才调用的接口。则能够直接调用,不须要放在ready函数中。所以说假设须要分享的东西不变,就直接放在ready事件处理函数去做号了(如上面demo的ready事件处理函数),假设要分享的东西一直在变,就像demo演示的那样动态的改动要分享的title

    demo演示(微信环境)

    7. TangIDE与微信SDK的结合

        上面的这些代码看似有点多,只是幸好强大的tangIDE已经帮我们完毕非常多工作了:
    - [1] 提供微信配置控件,自己主动完毕微信JSSDK环境搭建

            打开TangIDE在左側控件栏。SNS控件->微信设置直接拖入你须要调用jssdk接口的场景就可以。
    - [2] 各种SNS组件支持
            打开TangIDE在左側控件栏。SNS控件选择想要的组件直接拖入你须要调用的场景就可以。


    end

  • 相关阅读:
    有限制的最大连续和问题
    Codevs 5056 潜水员
    Codevs 1958 刺激
    Codevs 3731 寻找道路 2014年 NOIP全国联赛提高组
    [NOIP2014]解方程
    Codevs 3729 飞扬的小鸟
    Codevs 1689 建造高塔
    Codevs 2102 石子归并 2
    C语言基础之进制的那些事(1)
    指针
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/7152086.html
Copyright © 2020-2023  润新知