作为一名开发人员,我不禁要访问Jawbone UP掌握的大量步数和睡眠习惯数据。 数据太多了! 因此,我开始研究如何使用Jawbone UP API和Node提取数据。
我在网络上找到了如何使用Jawbone UP API和Node的示例,但是它们都很投入,涉及很多活动部件,以及一些过时的模块(例如Express的较早版本)。 在本文中,我想关注绝对的基础知识,而不必担心将用户数据保存到数据库,创建帐户或连接社交登录信息。 我们将重点介绍获取节点服务器以通过Jawbone API进行身份验证并返回用户数据所需了解的核心内容。
该演示的所有代码都可以在我们的GitHub存储库中找到 。
设置Jawbone应用
我们需要的第一件事是在我们的Jawbone帐户下设置一个新的Jawbone应用程序。 这是用户将被授权访问其数据的应用程序。
首先通过访问https://jawbone.com/up/developer并单击左下方的“登录”链接来登录Jawbone网站的开发人员部分。 您将不需要特定的Jawbone开发人员帐户,因为他们将允许您使用现有的Jawbone帐户登录。
登录后,转到https://jawbone.com/up/developer/account ,或单击“帐户”下左侧菜单中的“管理帐户”链接。
在此页面上,您将进入开发者帐户页面。 在这里,单击“创建应用程序”。
在加载的页面上,将提示您输入应用程序的详细信息:
- 名称 -您的应用程序的名称,我输入了“ Jawbone UP Node Demo”。
- 说明 –这是简短的说明,将出现在用户的UP App Gallery中。
- 详细说明 -它出现在应用程序库中该应用程序的详细信息页面上。
- 徽标 –为您的应用程序上传徽标。 如果您收到有关“选择”的错误(我知道这很奇怪,但是对跟随并获得该消息的少数人来说是有道理的),则可能是徽标徽标太大。
- URL –您应用的主页
- 授权URL –可以在其中找到登录页面的URL。 为了进行测试,请输入
https://localhost:5000/login/jawbone
。 - OAuth重定向URI –用户通过身份验证后,允许您的应用程序重定向到的URL。 在我们的演示中,我们将输入
https://localhost:5000
。
单击创建应用程序后,将直接转到包含应用程序列表的页面。 您新创建的应用应与我的相似:
记下“客户端ID”和“应用程序机密”,这是连接到Jawbone API所需要的。
启动我们的Node App
我将所有的Node服务器代码包含在一个名为server.js
文件中。 我们首先需要为服务器使用必要的npm模块。
首先,我们设置了基本的Express应用。
var express = require(‘express’),
app = express(),
然后,我们需要ejs
(嵌入式JavaScript),这使我们可以将JavaScript插入HTML模板中。 我们将使用它在返回的HTML中显示JavaScript变量。
ejs = require('ejs'),
为了能够通过Jawbone API进行身份验证并重定向回我们的应用程序,Jawbone要求我们通过https重定向到页面。 为此,我们必须包含https
。
https = require('https'),
接下来,我们包含fs
,它使我们可以读取文件系统。 我们需要它来读取服务器证书文件以启用https。
fs = require('fs'),
我们还将需要body-parser
来使我们能够处理JSON请求:
bodyParser = require('body-parser'),
Jawbone UP API使用OAuth 2.0协议进行身份验证。 基本上,这意味着要使用户使用其Jawbone帐户登录并授予我们访问其数据的权限,我们需要遵循此协议。 幸运的是,npm的passport
模块包含一个名为passport-oauth
模块,该模块支持此功能。 我们在OAuth 2.0的应用中设置了passport
,如下所示:
passport = require('passport'),
JawboneStrategy = require('passport-oauth').OAuth2Strategy,
然后,我们有了一个自我解释的变量,该变量存储了将要运行的端口。
port = 5000,
下一步,我们将在jawboneAuth
和OAuth 2.0 jawboneAuth
身份验证所需的所有值存储在jawboneAuth
。 这是您使用我们在注册应用程序时注意到的“客户端ID”和“应用程序秘密”值的时刻。
jawboneAuth = {
clientID: 'jUvu1_4u_mA',
clientSecret: '8961265d16ac678948006c2944ed85dbeeaab547',
authorizationURL: 'https://jawbone.com/auth/oauth2/auth',
tokenURL: 'https://jawbone.com/auth/oauth2/token',
callbackURL: 'https://localhost:5000/sleepdata'
},
以下是这些值的含义和/或来源的概述:
- clientID –这是为Jawbone应用程序列出的“客户端ID”。
- clientSecret –这是它下面的“ App Secret”值。
- authorizationURL –这是用户将被重定向到的UP OAuth 2.0身份验证页面的位置。
- tokenURL –这是Jawbone UP API中的URL,我们必须对其进行HTTPS调用才能请求访问令牌。 我们需要在对Jawbone UP API的调用中包括此令牌,以证明我们有权提出这些数据请求。 在Jawbone UP API中,此令牌有效期为一年,因此您可以将其存储在数据库中,并让用户连接到其Jawbone帐户一年,然后才需要重新进行身份验证。 在本教程中,我们不会考虑存储用户等,但是如果您想进一步推动这一点,请牢记。
- callbackURL –我们网站上的URL,一旦成功授予我们访问数据的权限,Jawbone会将用户引导回该URL。 对于我们来说,这是一个显示睡眠数据的页面。
我们要定义的最后一个变量是sslOptions
,其中包含我们需要提供给服务器的所有详细信息,以允许我们使用HTTPS运行该服务器。 在稍后的文章中,我将在解释我们如何设置HTTPS时详细介绍每一个。
然后,我们包含几行代码,定义了一些基本的Node应用程序功能,Node开发人员将很熟悉这些功能:
app.use(bodyParser.json());
app.use(express.static(__dirname + '/public'));
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
- bodyParser –允许我们解析JSON对象。
- 静态文件夹 –定义我们的静态文件(如图像)在服务器上的位置(在本例中为
/public
文件夹)。 - EJS –将
ejs
模块分配为我们的模板引擎。 - Views文件夹 –定义我们的
ejs
视图文件在服务器上的位置(在本例中为/views
文件夹)。
为了在Express中初始化Passport,我们运行以下行:
app.use(passport.initialize());
需要注意的是,如果我们要进行持续的登录会话,则需要在Passport中进行更多设置。 在这种情况下,我们将需要设置会话。 但是,在本教程中,我们将只专注于从Jawbone UP API提取数据的初始阶段,而不必担心登录会话。
设置我们的GET请求
为了将用户定向到Jawbone UP API的登录屏幕,我们需要在服务器上分配一个URL,该URL将重定向到Jawbone登录屏幕。 下面的代码为此指定了/login/jawbone
的URL。 在对此URL的GET请求中,我们调用passport.authorize()
来打开我们的Jawbone UP授权页面:
app.get('/login/jawbone',
passport.authorize('jawbone', {
scope: ['basic_read','sleep_read'],
failureRedirect: '/'
})
);
如您在上面看到的,我们有一系列要请求的特定权限- ['basic_read','sleep_read']
。 就我们而言,我们需要基本的用户数据和睡眠数据。 如果您想请求访问步数,进餐等,则可以向该数组添加其他权限请求。 您可以在Jawbone UP开发人员认证文档页面上查看可用列表以及它们提供的访问权限列表。
另请注意,如果Jawbone UP身份验证屏幕中的身份验证失败,则会将我们重定向回首页。 passport
模块中也可以设置成功successRedirect
,但是我发现使用Jawbone UP API,是不需要的,因为我们将在此代码中的JawboneStrategy
定义回调URL。
然后,我们设置GET请求,使我们的睡眠数据显示在该请求上。 当我们可以访问用户数据时,这就是告诉API将我们重定向到的位置。 在此示例中,它是/sleepdata
:
app.get('/sleepdata',
passport.authorize('jawbone', {
scope: ['basic_read','sleep_read'],
failureRedirect: '/'
}), function(req, res) {
res.render('userdata', req.account);
}
);
我们在这里具有相同的passport.authorize()
函数,只是为了检查用户在访问该页面时是否已登录。 如果是这样,我们运行res.render('userdata', req.account);
它将Jawbone UP API返回的数据传递到userdata.ejs
模板(我们将很快设置)。 如果未登录,它们将被引导回Jawbone UP身份验证屏幕。
然后,我们设置一个URL,以允许用户从/logout
,一旦/logout
,该用户便将用户重定向回首页:
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
最后,对于路由,我们将其设置为在有人尝试访问主页时加载index.ejs
模板:
app.get('/', function(req, res) {
res.render('index');
});
使用Passport连接到Jawbone UP API
代码中最大的一块也是最重要的-设置Passport“策略”以告诉Passport如何处理使用'jawbone'
进行授权的请求。 看起来像这样:
passport.use('jawbone', new JawboneStrategy({
clientID: jawboneAuth.clientID,
clientSecret: jawboneAuth.clientSecret,
authorizationURL: jawboneAuth.authorizationURL,
tokenURL: jawboneAuth.tokenURL,
callbackURL: jawboneAuth.callbackURL
}, function(token, refreshToken, profile, done) {
var options = {
access_token: token,
client_id: jawboneAuth.clientID,
client_secret: jawboneAuth.clientSecret
},
up = require('jawbone-up')(options);
up.sleeps.get({}, function(err, body) {
if (err) {
console.log('Error receiving Jawbone UP data');
} else {
var jawboneData = JSON.parse(body).data;
for (var i = 0; i < jawboneData.items.length; i++) {
var date = jawboneData.items[i].date.toString(),
year = date.slice(0,4),
month = date.slice(4,6),
day = date.slice(6,8);
jawboneData.items[i].date = day + '/' + month + '/' + year;
jawboneData.items[i].title = jawboneData.items[i].title.replace('for ', '');
}
return done(null, jawboneData, console.log('Jawbone UP data ready to be displayed.'));
}
});
}));
让我们回顾一下所有这些代码在做什么。
首先,我们从文件开头定义的jawboneAuth
对象中设置clientID
, clientSecret
, authorizationURL
, tokenURL
和callbackURL
。 这是使用new JawboneStrategy()
。
接下来,我们有处理此数据的回调函数。 我们在此回调函数中使用token
和done
值。 token
是Jawbone UP API访问令牌,我们需要在对API的任何调用中包括该令牌,以证明我们已通过身份验证。 done
是将我们的数据返回到应用程序的回调函数。
我们将访问令牌以及先前定义的客户端ID和密码传递到options对象中的jawbone-up
:
var options = {
access_token: token,
client_id: jawboneAuth.clientID,
client_secret: jawboneAuth.clientSecret
},
up = require('jawbone-up')(options);
jawbone-up
模块是Node模块,它使我们可以访问Jawbone UP API端点。 这些是我们对API进行的返回用户数据的调用(例如GET https://jawbone.com/nudge/api/v.1.1/users/@me/sleeps
),但是jawbone-up
模块允许我们访问这些在诸如up.moves.get()
和up.sleeps.get()
函数中。 在我们的示例中,我们将使用up.sleeps.get()
获取睡眠数据。
在up.sleeps.get()
我们有两个变量err
和body
。 如果从API接收数据时出错,那么它将在err
变量中返回,因此我们在回调开始时进行测试。
否则,我们将在body
变量中的JSON字符串中返回数据。 body
变量将包含一个JSON字符串值,如下所示:
{
"meta": {
"user_xid": "Hllksn238c-KJBu2esff_Q",
"message": "OK",
"code": 200,
"time": 1428562859
},
"data": {
"items": [
{
"time_updated": 1428534140,
"xid": "8060gi-3V-kLT-niK4ZxB2NLqnct9_2B",
"title": "for 7h 45m",
"time_created": 1428504300,
"time_completed": 1428533100,
"details": {
"body": 0,
"sound": 15000,
"tz": "Australia/Sydney",
"awakenings": 0,
"light": 12900,
"mind": 0,
"asleep_time": 1428505800,
"awake": 1500,
"rem": 0,
"duration": 28800,
"smart_alarm_fire": 0,
"quality": 84,
"awake_time": 1428533100,
"sunrise": 1428524040,
"sunset": 1428565320
},
"date": 20150409,
"shared": true,
"sub_type": 0
},
{
"time_updated": 1428447559,
"xid": "8060gi-3V-nmNeDAWAAXjwzpZx2RQOgg",
"title": "for 7h 38m",
"time_created": 1428418945,
"time_completed": 1428447488,
"details": {
"body": 0,
"sound": 13985,
"tz": "Australia/Sydney",
"awakenings": 1,
"light": 13501,
"mind": 0,
"asleep_time": 1428419639,
"awake": 1057,
"rem": 0,
"duration": 28543,
"smart_alarm_fire": 0,
"quality": 78,
"awake_time": 1428447300,
"sunrise": 1428437580,
"sunset": 1428478980
},
"date": 20150408,
"shared": true,
"sub_type": 0
}
],
"links": {
"next": "/nudge/api/v.1.1/users/Hllksn238c-KJBu2esff_Q/sleeps?page_token=1427987112334&limit=10"
},
"size": 10
}
}
我们想要的一切都在data
之内。 我们使用JSON.parse(body)
将上面的值解析为一个JavaScript对象,并将data
键中的值分配给一个名为jawboneData
data
的变量:
var jawboneData = JSON.parse(body).data;
然后,我们有一个for循环,该循环遍历data
中数组中的每个项目,并格式化我们的日期和睡眠时间,然后再将它们返回到模板中进行显示。
var date = jawboneData.items[i].date.toString(),
year = date.slice(0,4),
month = date.slice(4,6),
day = date.slice(6,8);
jawboneData.items[i].date = day + '/' + month + '/' + year;
在这里,我们读取日期,将其转换为字符串,然后自己切出日期,月份和年份。 它以20150408
的值返回,因此我们将前四个数字切成年,将其后的两个切成月,将后两个切成日。 然后,我们将其排列为DD/MM/YYYY
,如果您希望将其格式化为美国日期格式,则可以切换月份和日期:
jawboneData.items[i].date = month + '/' + day + '/' + year;
Jawbone API返回一个格式比较好的睡眠持续时间值作为title
,如下所示: "for 9h 43m"
。 我们可以使用它,但是像这样删除"for "
部分:
jawboneData.items[i].title = jawboneData.items[i].title.replace('for ', '');
然后,我们将该数据返回到Passport的回调函数,该函数将呈现我们的userdata.ejs
。 为此,我们将jawboneData
变量返回到done
函数。 还有一个console.log
,以便我们可以在日志中看到发送Jawbone UP数据以显示它们的时间:
return done(null, jawboneData, console.log('Jawbone UP data ready to be displayed.'));
使用HTTPS
如前所述,为了使用Jawbone UP API,我们需要使用HTTPS运行服务器,因为Jawbone的服务要求双方都运行HTTPS。 如果callbackURL
未设置为https
,则尝试登录时将收到“无效重定向”错误。
为了使我们的示例生效,我们将使用自签名证书。 如果您是在实时站点上执行此操作,则需要从有效的证书颁发机构获取正确的证书。
在server.js
,我们定义了两个SSL选项:
sslOptions = {
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.crt')
};
这些是我们两个与身份验证相关的文件在服务器上的文件位置:
- 密钥 –这是我们服务器的私钥
- cert –这是我们的自签名证书
为我们的服务器生成一个私钥
要生成私钥,我们需要使用OpenSSL Toolkit 。 Mac OSX和Linux用户应预先安装此软件。 对于Windows用户,您可以安装Cygwin ,在“选择软件包”屏幕上搜索“ openssl”,然后选择出现的软件包。
我们可以通过打开终端,导航到服务器的文件夹并运行以下命令来生成该私钥:
openssl genrsa -out server.key 2048
这将生成一个准备使用的私有服务器密钥,称为server.key
。
生成证书签名请求(CSR)
然后,我们需要生成一个CSR。 通常会将其发送给证书颁发机构,但在我们的情况下,我们将自己对其进行签名以进行测试。
要使用上面创建的私钥生成CSR,请运行以下命令:
openssl req -new -key server.key -out server.csr
您将得到一个要回答的问题列表,回答这些问题,您将收到名为server.csr
的文件的CSR。
使用我们的服务器私钥生成签名证书
最后,要生成没有证书颁发机构的自签名证书,我们运行以下命令来生成有效期为一年的证书:
openssl x509 -req -in server.csr -out server.crt -signkey server.key -days 365
该命令应该已经生成了server.crt
文件-这是您的证书。
删除我们的证书申请
对于那些希望保持整洁并自我签名证书的人,我们可以删除server.csr
因为现在已经对我们的证书进行了签名。
我们已经准备好HTTPS
准备好我们的私钥和证书并在Node文件中对其进行定义后,我们的服务器即可作为HTTPS运行。 以下代码使用HTTPS和我们的sslOptions启动服务器:
var secureServer = https.createServer(sslOptions, app).listen(port, function(){
console.log('UP server listening on ' + port);
});
我们的EJS文件
该应用程序的HTML都位于.ejs
文件中,以便在需要时可以在其中包含JavaScript变量。 这些文件都在/views
。 index.ejs
非常简单,仅包含标题,说明和登录按钮,该按钮将转到/login/jawbone
:
<body>
<h1>Jawbone UP Sleep Data</h1>
<p>Log in to see your latest sleep data.</p>
<a href="/login/jawbone" class="btn">Login</a>
</body>
userdata.ejs
是操作所在的位置。 我们可以关注的主要数据是表格:
<table class="sleep-table">
<thead>
<tr>
<th>Date</th>
<th>Sleep time</th>
</tr>
</thead>
<tbody>
<% for (var i=0; i<items.length; i++) { %>
<tr>
<td><%= items[i].date %></td>
<td><%= items[i].title %></td>
</tr>
<% } %>
</tbody>
</table>
对于EJS的新手,我们将JavaScript嵌入在<%
和%>
标记内。
我们将items
传递给userdata
模板,我们使用如下的for循环对其进行迭代: <% for (var i=0; i<items.length; i++) { %>
。
然后使用<%= items[i].date %>
和<%= items[i].title %>
每个日期和标题插入到我们的HTML中。
我们的应用程序在行动
要运行该应用程序,请转到您的终端并运行:
node server.js
运行它后,转到http://localhost:5000
,您将看到我们的初始页面:
如果单击登录按钮,将转到http://localhost:5000/login/jawbone
,它将把我们定向到Jawbone UP身份验证页面。 该页面将提示我们输入Jawbone登录详细信息。 输入这些详细信息后,或者如果您已经登录到Jawbone网站,您将被定向到Auth页面,请求访问您的用户数据。 点击“同意”:
当单击“同意”时,应返回到http://localhost:5000/sleepdata
页面,并返回我们的睡眠数据表:
如果单击“注销”按钮,它将注销我们并将其重定向回主页。
结论
这样就完成了对连接到Jawbone UP API以及将数据返回到Node服务器的基础知识的概述。
从这里开始的下一步可能包括建立数据库以存储数据供将来使用,为应用程序创建用户帐户,扩展从UP API中提取的数据量,更改其显示方式(也许添加一些漂亮的图形! ) 和更多。 将该数据与任意数量的其他API结合使用,对于某些真正精巧的应用程序来说,潜力是巨大的!
其他有用的资源
From: https://www.sitepoint.com/connecting-jawbone-up-api-node-js/