微信JSSDK使用指南
这篇文章主要来说说微信JSSDK的一些配置和微信分享的使用,包括从前端sdk文件引入到server端和微信server的交互。
另外Tangide已经把一些工作做好了。我会简要说一说怎么把它用起来。在开发的过程中主要參考了微信官方开发文档,因为微信提供的功能略多,我会定期更新文档,假设你已经熟悉了微信JSSDK的开发流程能够直接跳过前面的步骤,直接跳转到Tangide与微信JSSDK的使用结合 :)。
1. 首先得有个公众号
微信把公众号细分为服务号和订阅号。
接口权限各不同样,假设你记不住你的公众号有没有相应功能的权限,能够到开发人员中心->接口权限表相应查看。
2. 配置JS接口安全域名
微信限制了JS接口使用的域名。详细在:公众号设置->功能设置页面。好在能够同一时候配置3个,并且能够改动:
这意味着配置能够复用了。反复劳动降低了:)。
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_token
。access_token
有效时间为2个小时。向微信server请求频率限制为:每天2000次。微信还有其它一些參数的频率限制,你能够看这里。
以下来说说获取配置信息的详细步骤:
来看看server端是怎么处理的:
nodejs
server端:
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
需求能够自行注入函数来处理。
php
server端
<?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×tamp=$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(/^2\d\d$/)) {
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
。
7. TangIDE与微信SDK的结合
上面的这些代码看似有点多,只是幸好强大的tangIDE已经帮我们完毕非常多工作了:
- [1] 提供微信配置控件,自己主动完毕微信JSSDK环境搭建
打开TangIDE在左側控件栏。SNS控件->微信设置直接拖入你须要调用jssdk接口的场景就可以。
- [2] 各种SNS组件支持
打开TangIDE在左側控件栏。SNS控件选择想要的组件直接拖入你须要调用的场景就可以。
end
微信JSSDK使用指南的更多相关文章
- 微信JS-SDK开发 入门指南
目录 前言 1. 过程 1.1 代码 1.2 代理 1.3 下载 1.4 解压 1.5 运行 1.6 查看 2. 微信接口测试 2.1 申请测试帐号 2.1.1 测试号信息 2.1.2 接口配置信息 ...
- 基于Node.js的微信JS-SDK后端接口实现
做了一个网站,放到线上,用微信打开,点击分享,可是分享后发给朋友的链接卡片是微信默认自带的,如下: 这标题,描述以及图片是默认自带的,丑不说,分享给别人还以为是盗号网站呢,而接入微信的JSSDK后,分 ...
- C#开发微信门户及应用(39)--使用微信JSSDK实现签到的功能
随着微信开逐步开放更多JSSDK的接口,我们可以利用自定义网页的方式来调用更多微信的接口,实现我们更加丰富的界面功能和效果,例如我们可以在页面中调用各种手机的硬件来获取信息,如摄像头拍照,GPS信息. ...
- 微信JS-SDK坐标位置转换为百度地图坐标
微信JS-SDK开发过程中,使用getLocation获取坐标位置,如何将微信获取的坐标直接应用到百度地图中,显示以下效果: 说明:红色图标是从微信转换过来的位置,蓝色图标是周边位置.首先从微信开发流 ...
- 前端工程师如何快速的开发一个微信JSSDK应用
亲们,订阅号出来已经很久了,作为一个前端工程师或者全栈工程师,你是不是错过了什么?大概许多攻城狮同砚还没有反应过来订阅号怎么回事,就马上要被微信的应用号秀一脸了.在应用号还没有正式出来之前,我们赶紧一 ...
- 微信公众平台开发 微信JSSDK开发
根据微信开发文档步骤如下: 1.先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”. JS接口安全域名设置 mi.com(前面不用带www/http,域名必须备案过) 2.引 ...
- 微信JSSDK配置文件说明
微信JSSDK配置文件说明 1.官方配置 wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开, ...
- C#微信开发-微信JS-SDK(1)之通过config接口注入权限验证配置
官方文档是微信JS-SDK的使用步骤http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#JSSDK.E4.BD.B ...
- 微信JS-SDK DEMO页面和示例代码
<?php require_once "jssdk.php"; $jssdk = new JSSDK("yourAppID", "yourApp ...
随机推荐
- js 脚本语言
字符串转换为数字 parseInt(string) .parseFloat().Number() 参考博客:https://zhidao.baidu.com/question/629898532158 ...
- C#override与new修饰隐藏的区别(转载)
C#比java多一个new隐藏的功能.C# override重写相当于java中没有关键字的方法重写.所以java中方法是没有隐藏功能的. C# override重写,是指对父类中的虚方法(标记vir ...
- bzoj3994: [SDOI2015]约数个数和(反演+结论?!)
这题做的历程堪称惊心动魄 刚刚学了莫比乌斯反演的我高高兴兴的和cbx一起反演式子 期间有突破,有停滞,有否定 然后苟蒻的我背着cbx偷偷打开了题解 看到了 我...... 去你的有个性质啊(当然还是自 ...
- CAD参数绘制多行文字(网页版)
在CAD设计时,需要绘制多行文字,用户可以设置设置绘制文字的高度等属性. 主要用到函数说明: _DMxDrawX::DrawMText 绘制一个多行文字.详细说明如下: 参数 说明 DOUBLE dP ...
- python 弹窗
import ctypes message = ctypes.windll.user32.MessageBoxA(0,'message','tips',0)
- JAVA基础——生产者消费者问题
1.生产者消费者问题:经典案例 生产者和消费者问题是操作系统的经典问题,在实际工作中也常会用到,主要的难点在于协调生产者和消费者,因为生产者的个数和消费者的个数不确定,而生产者的生成速度与消费者的消费 ...
- jQuery任意标签锚点跳转插件
// 任意锚点平滑跳转插件// 2010-07-15 v1.0(function($){ $.fn.zxxAnchor = function(options){ var defaults = { ie ...
- BZOJ 3876 支线剧情 有源汇有上下界最小费用可行流
题意: 给定一张拓扑图,每条边有边权,每次只能从第一个点出发沿着拓扑图走一条路径,求遍历所有边所需要的最小边权和 分析: 这道题乍一看,可能会想到什么最小链覆盖之类的,但是仔细一想,会发现不行,一是因 ...
- BZOJ 1565 植物大战僵尸 最大权闭合子图+网络流
题意: 植物大战僵尸,一个n*m的格子,每 个格子里有一个植物,每个植物有两个属性: (1)价值: (2)保护集合,也就是这个植物可以保护矩阵中的某些格子. 现在你是僵尸,你每次只能从(i,m) 格子 ...
- 零基础入门学习Python(19)--函数:我的地盘听我的
知识点 函数与过程 在许多编程语言中,函数(function)是有返回值的,过程(procedure)是简单.特殊并且没有返回值的.而在Python中,严格来说只有函数没有过程. 例如: >&g ...