免责声明:ionic 服务目前处于 alpha 状态。当 ionic 的推送服务成为黄金时,我正在谈论的功能肯定会存在,但我今天所涵盖的 细节 肯定会发生变化。请记住这一点。
在准备下周的 演讲 时,我一直在研究各种离子服务。我尚未使用的 ionic 推送服务的一个方面是 webhooks 。 ionic 允许您定义一个 webhook(实际上是一个 url),只要有人注册推送、取消注册推送,或者只是有一个设备变得无效,他们就会调用它。我想我会创建一个快速的 node.js 应用程序,这样我就可以自己测试这个功能。为了托管此应用程序,我将使用 bluemix ,这是我们的 paas 解决方案,它使 node.js 托管变得非常容易。另外,bluemix 本身支持推送,您可能希望使用它而不是 ionic 的推送服务。 bluemix 的优点之一是能够按照您认为合适的方式混合和匹配服务。
首先,我创建了一个简单的推送演示。您可以在此处找到此演示的完整源代码:https: //github.com/cfjedimaster/ionicservicespresentation/tree/master/demos/push1_user 。我不会在博客条目中显示代码,因为它不一定相关。它所做的只是注册应用程序以进行推送并将其与离子用户相关联。这本身很有趣,因为文档还没有显示完整的示例,但我将在本周晚些时候将其保存在博客条目中。重要的是我有一个应用程序可以在我的设备上启动并进行真正的推送测试。
现在让我们将注意力转向
webhook
功能。此功能的主要目的是让您能够知道用户何时注册(或注销)推送。你可以使用
任何
你想要的服务器技术,对我来说就是 node.js。我首先转到 bluemix 并登录(顺便说一句,这是免费的!)并使用 node.js starter 创建一个新应用程序。我下载了示例代码(自从我第一次开始使用 bluemix 以来,它变得相当简单),并运行
npm install
以在本地准备好东西。我之前讨论过如何将 bluemix 用于 node.js,但如果您需要复习,请在此处查看我的文章:
在 bluemix 上托管 node.js 应用程序
。
对于我的应用程序,我决定它将通过将注册数据存储在 cloudant 中来响应 ionic 的调用。有一个很棒的 node.js 包 ,所以我知道在应用程序中使用它会很简单。您可以从目录轻松地将 cloudant 服务添加到您的 bluemix 应用程序:
添加服务后,我进入管理员并创建了一个名为“registrations”的数据库。现在我打开我的代码并开始编写。信不信由你,我一口气写下了下面的所有内容,而且我没有犯任何错误。严重地。 (好的,我可能会偏离十倍左右。)这是我用来处理来自 webhook 的调用的代码:
/*eslint-env node*/
var express = require('express');
// cfenv provides access to your cloud foundry environment
// for more info, see: https://www.npmjs.com/package/cfenv
var cfenv = require('cfenv');
var cloudant = require('cloudant');
var cdme = "";
var cdpassword = "";
if(process.env.vcap_services) {
var info = json.parse(process.env.vcap_services);
cdme = info.cloudantnosqldb[0].credentials.username;
cdpassword = info.cloudantnosqldb[0].credentials.password;
} else {
cdme = "the username from the bluemix console";
cdpassword = "ditto";
}
var cloudant = cloudant({account:cdme, password:cdpassword});
var registrationdb = cloudant.db.use('registrations');
var app = express();
// serve the files out of ./public as our main files
app.use(express.static(__dirname + '/public'));
var jsonbody = require("body/json");
// get the app environment from cloud foundry
var appenv = cfenv.getappenv();
function getregistrationmode(body) {
if(body.token_invalid) return "invalid";
if(body.unregister && body.unregister == true) return "unregister";
return "register";
}
app.post('/register', function(req, res) {
console.log('running register');
jsonbody(req, function(err,body) {
console.log(body);
var tokens = [];
/*
there are 3 'modes' of the hook: register, invalid, unregister,
but there is no simple flag for this. we'll do the ugly code in a
function and when ionic improves this, we can fix it there.
*/
var mode = getregistrationmode(body);
console.log('registration mode is '+mode);
if(mode === 'register') {
if(body._push.android_tokens) {
body._push.android_tokens.foreach(function(token) {
tokens.push(token);
});
}
if(body._push.ios_tokens) {
body._push.ios_tokens.foreach(function(token) {
tokens.push(token);
});
}
console.log("going to add tokens "+tokens);
tokens.foreach(function(token) {
//do a get to see if exists
registrationdb.get(token, function(err, dbbody) {
if(err) {
console.log('inserting '+token);
//console.log(arguments);
registrationdb.insert({_id:token,time:new date().gettime()}, function(err, body, header) {
//console.log("cloudant db response: "+json.stringify(arguments));
});
} else {
console.log('updating '+token);
registrationdb.insert({_id:token,time:new date().gettime(),_rev:dbbody._rev}, function(err, body, header) {
//console.log("cloudant db response: "+json.stringify(arguments));
});
}
});
});
} else {
//for both unregister, invalid we just delete, but how we get the tokens is different
if(mode === "unregister") {
if(body._push.android_tokens) {
body._push.android_tokens.foreach(function(token) {
tokens.push(token);
});
}
if(body._push.ios_tokens) {
body._push.ios_tokens.foreach(function(token) {
tokens.push(token);
});
}
} else {
if(body.android_token) tokens = [body.android_token];
if(body.ios_token) tokens = [body.ios_token];
}
console.log("removing "+tokens);
tokens.foreach(function(token) {
//do a get to see if exists
registrationdb.get(token, function(err, dbbody) {
if(!err){
registrationdb.destroy(token,dbbody._rev, function(err, body, header) {
//console.log("cloudant db response: "+json.stringify(arguments));
});
}
});
});
}
});
res.send(1);
});
app.get('/list', function(req, res) {
var results = [];
registrationdb.list(function(err, body) {
if(!err) {
body.rows.foreach(function(doc) {
results.push(doc);
});
res.send(json.stringify(results));
}
});
});
// start server on the specified port and binding host
app.listen(appenv.port, function() {
// print a message when the server starts listening
console.log("server starting on " + appenv.url);
});
在我们深入探讨之前,让我先澄清一下,我在这里写了 最低限度的 代码以使我的测试正常运行。这段代码可以组织得更好。对不起。
让我们自上而下地工作。大多数情况下,前几行只是简单的 require 语句。请注意,我通过环境变量或硬编码值获取了我的 cloudant 凭证。不要忘记,您可以通过单击服务本身的“显示凭据”链接在 bluemix 控制台中获取您的凭据:
继续往下 - 下一个关键方面是
getregistrationmode
。如果您阅读有关 ionic 将如何访问您的 url 的文档,您会注意到很难区分一种“模式”和另一种。我和这项服务背后的开发人员谈过,他同意它可以更简单,但现在,我写了一个简单的函数来查看数据并找出模式。在
/register
路由中,我们然后使用该函数来弄清楚我们到底在做什么。
如果我们正在添加,那么我们将获取令牌并确定它们是否已经存在于我们的数据库中。如果他们这样做,我们更新,如果他们不这样做,我们插入。我在注册时附上了时间戳。我也可以包括其他东西。 “取消注册”和无效状态我们最终都删除了令牌。
最后,我构建了一个
/list
路由,这样我就可以快速查看它是否正常工作。就是这样。正如我所说,我一口气写完了所有这些,没有任何错误。 (实际上我做了一些非常愚蠢的事情——阅读底部的 ps 了解详情。)
我首先在我的本地机器上运行它,为了测试,我从 ionic 的文档中复制了示例 json 数据包,并使用 postman chrome 应用程序 进行测试。邮递员已经存在多年了,但直到昨天我才开始玩它。太棒了。这是在行动:
我真的不能称赞这个应用程序。它当然不是“每天都使用”类型的东西,但它非常有用。一旦我的应用程序运行良好,我将其推送到 bluemix:
cf push ionicpushregistration
。我等待它进行设置,然后在命令行运行它以告知 ionic 关于 webhook 的信息:
ionic push webhook_url http://ionicpushregistration.mybluemix.net/register
在我写这篇文章时,您无法确定离子应用程序是否正在使用网络挂钩。再次,我已将此报告给 ionic,他们知道这是一个问题。
就是这样。除了来自 ionic 的数据包需要一些组织外,该功能按预期工作。你可以自己点击 /list url 并查看我测试过的令牌。
一旦 ionic 的服务达到 1.0,我将考虑发布一个完整的 node.js 应用程序来处理这个问题。只是提醒我!
ps 所以是的,我的“史诗”搞砸了。我试图编写这样的代码:“如果我有一个用于我的 cloudant auth 废话的环境变量,请使用它,否则默认为这些值。”在这样做的同时,我最终完全破坏了 process.env,将其设置为一个新对象,该对象擦除了其他所有内容。这有点搞砸了很多事情。是的,我仍然不喜欢节点。