转载

使用 Bluemix 和 MEAN 堆栈构建自助发表 Facebook 信息的应用程序,第 1 部分:使用 Facebook 作为...

有时用户希望当他们不在线时,服务器会代表他们向 Facebook 发表帖子。例如,业务页面的所有者可能希望在某款产品的库存不多时发布公告,鼓励顾客在还有货时尽快购买。或者一个人可能希望他的时间表以随机的间隔发布消息。

可以编写一个服务器应用来实现此目的,但这么做并不容易。在这个 3 教程系列文章中,我将展示如何使用 IBM Bluemix 作为云提供商来实现此目的。本系列还会介绍 MEAN 堆栈所有 4 个组件的基本知识。为了演示此功能,我将展示如何构建一个应用程序,在随机的时间代表用户发表笑话。

  • 第 1 部分(本教程)介绍如何使用 Facebook 作为登录来源和身份验证机制。
  • 第 2 部分 介绍如何配置 MongoDB 来存储从 Facebook 获取的用户信息。
  • 第 3 部分 介绍如何使用 Facebook REST API 让服务器充当用户。

备注:Bluemix 包含一个 Single Sign On 服务,该服务可从 Facebook 以及其他来源接收登录信息。但是,我在本教程中不会使用它。此处说明的应用程序类型与 Facebook 具有紧密的联系,所以让用户使用其他帐户登录没什么帮助。

构建您的应用程序所需的准备工作

  • 一个 Bluemix 帐户。
  • HTML 和 JavaScript 的知识。
  • 一个 Facebook 帐户。
  • 一个可将 Node.js 应用程序上传到 Bluemix 的开发环境,比如 Eclipse。要使用 Eclipse,请参阅 “ 使用 IBM Eclipse Tools for Bluemix 部署应用程序 ”。

获取代码

Facebook API

有两个 API 可供 Facebook 应用程序开发人员使用:

  • 浏览器 API
  • Web 服务器 API

浏览器 API

这个 Facebook 标准 API 假设用户已通过浏览器登录到应用程序,并通过在浏览器中执行的 JavaScript 代码来完成大部分操作。

从安全的角度讲,这是一种出色的机制,因为它让 Facebook 承载的库能够直接联系用户来要求获得权限。但是它不适合本教程的用途。我们需要在用户未登录时系统能代表用户执行操作。

Web 服务器 API

还可以直接从服务器使用的另一个 API。它使用基于 HTTP 请求的 REST 架构,这使得它更难使用,但是可以确保您能从任何现代服务器编程中环境获得它。这是本系列文章中使用的主要接口。

第 1 步. 创建一个 Node.js Bluemix 应用程序

首先登录 Bluemix 并创建一个 Node.js 应用程序。请参阅 developerWorks 上的IBM Bluemix 页面 获取帮助。

本教程假设您正在修改默认的 Node.js 入门应用程序。

第 2 步. 创建一个 Facebook 应用程序

按照以下步骤在 Facebook 中创建应用程序:

  1. 登录 Facebook。
  2. 找到 Facebook 应用程序仪表板 ,然后单击 Add a New App
  3. 选择 Website
  4. 命名应用程序(我使用的名称是 “JokeSender”),然后单击 Create New Facebook App ID
  5. 选择一个类别,然后单击 Create App ID
  6. 对于站点 URL 和移动站点 URL,输入您在第 1 步中创建的应用程序的主 URL。在我的例子中,URL 是 http://using-facebook.mybluemix.net/。
  7. 您将看到此时显示了一些 JavaScript 代码,其内容看起来应与下面的代码类似。复制这些代码并将其粘贴在某处;在后面的第 9 步中需要它。
    <script>    window.fbAsyncInit = function() {     FB.init({      appId      : '833955603319525',      xfbml      : true,      version    : 'v2.3'     });    };     (function(d, s, id){     var js, fjs = d.getElementsByTagName(s)[0];     if (d.getElementById(id)) {return;}     js = d.createElement(s); js.id = id;     js.src = "//connect.facebook.net/en_US/sdk.js";     fjs.parentNode.insertBefore(js, fjs);    }(document, 'script', 'facebook-jssdk'));   </script>
  8. 单击 Next
  9. 在开发环境中,向代码中添加您刚复制的 Like 按钮的 HTML。
    <div class="fb-like", data-share="true" data-width="450", data-show-faces="true"> </div>
  10. 将 public/index.html 文件的正文替换为第 9 步的结果,内容与下面这段代码类似。确保使用您自己的 appId 编号,而不是我的。
    <script>  window.fbAsyncInit = function() {   FB.init({    appId      : '833955603319525',    xfbml      : true,    version    : 'v2.3'   });  };   (function(d, s, id){   var js, fjs = d.getElementsByTagName(s)[0];   if (d.getElementById(id)) {return;}   js = d.createElement(s); js.id = id;   js.src = "//connect.facebook.net/en_US/sdk.js";   fjs.parentNode.insertBefore(js, fjs);  }(document, 'script', 'facebook-jssdk')); </script>  <div class="fb-like", data-share="true" data-width="450", data-show-faces="true"> </div>
  11. 运行应用程序。确保它拥有 Like 按钮。

出现 Bluemix 服务器错误时的解决办法(在 Eclipse 中)

有时 Bluemix 和 Eclipse 不同步,而且在尝试上传和运行应用程序时会出现服务器错误。

  1. 在快速访问区域中,键入 server
  2. 选择 Views > Servers
  3. 右键单击 IBM Bluemix ,然后选择 Update Password
  4. 在两个字段中键入您的 IBM 密码,然后单击 OK
  5. 此刻不要执行密码恢复,单击 No
  6. 尝试再次运行应用程序。
  7. 如果应用程序无法运行,请展开 Bluemix 服务器视图,删除该应用程序,然后再试一次。

第 3 步. 启用 Facebook 中的应用程序登录

下一步是让用户通过 Facebook 登录您的应用程序。

  1. 返回 Facebook 应用程序仪表板 。
  2. 选择您的应用程序。
  3. 单击 Show 并记下 App ID 和 App Secret。
  4. 单击 Advanced 选项卡。
  5. 在 OAuth Settings 标题下,将 Client OAuth Login 和 Embedded browser OAuth Login 都设为 Yes
  6. 在 Valid OAuth redirect URIs 字段中,键入:
    https://<application name>.mybluemix.net/index.html
    一般而言,加密对应用程序的访问是个不错的想法。通过仅指定 https ,您可确保您的应用程序不支持未加密的登录。
  7. 单击 Save Changes

第 4 步. 运行初始应用程序

下一步是让用户使用客户端 JavaScript 接口,从应用程序的网页登录 Facebook。这也是您开始使用一些库的地方,比如 Angular 和 Bootstrap。我们对默认应用程序进行了太多的更改,无法一一列出,所以我在这里提供了修改后的代码并将解释放在注释中。

  1. 创建一个名为 public/scripts 的目录。
  2. 删除两个目录:public/images 和 public/stylesheets。
  3. 创建一个名为 public/scripts/datamodel.js 的文件夹,其中包含以下代码:
    // Angular data model  // Create a new Angular application var myApp = angular.module("myApp", []);   // Define the controller for Facebook interaction. The // controller also contains scope, which includes the // data model. myApp.controller("facebookCtrl", function($scope) {    // For now, have one string in the data model,  // fbStatus. It will contain the status of the  // Facebook communication  $scope.fbStatus = ""; });   // This function sets the fbStatus variable to the parameter. // It is useful to have this function so that the rest of // the JavaScript code would be able to set the value of // $scope.fbStatus without having to know anything about // Angular. var setFacebookStatus = function(status) {  var scope = angular.element($("#facebookCtrl")).scope();    // scope.$apply takes a function because of re-entrancy.  // The browser may not be able to handle changes in the  // scope variable immediately, in which case the function  // will be executed later.  scope.$apply(function() {   scope.fbStatus = status;  }); };
  4. 创建一个名为 public/scripts/facebook.js 的文件,它将包含以下代码(一定要将 FB.init 中的 appID 改为您自己的应用程序值):
    // This function is called during initialization and after // the user clicks the logon button. function checkLoginState() {   // Ask Facebook about the currently logged in user, and  // call statusChangeCallback when you get the response.  FB.getLoginStatus(function(response) {   statusChangeCallback(response);  }); }   // This function is called by FB.getLoginStatus after it gets the // results. function statusChangeCallback(response) {  // The response object is returned with a status field that lets the  // app know the current login status of the person.  //  // The response object can be found in the documentation  // for FB.getLoginStatus().    if (response.status === 'connected') {   // Logged into your app and Facebook.   loggedOn();  } else if (response.status === 'not_authorized') {   setFacebookStatus("Please authorize this application");  } else {   // Not logged into Facebook   setFacebookStatus("Please log into Facebook");  } }   // Facebook API initialization function. This function is called // after the Facebook API code is downloaded from Facebook's site. window.fbAsyncInit = function() {    // Initialize the Facebook SDK. Make sure to change appId to your  // value.  FB.init({   appId      : '833955603319525',   cookie     : true,  // enable cookies to allow the server to access        // the session   xfbml      : true,  // parse social plugins on this page   version    : 'v2.2' // use version 2.2  });   // Check logged in status  checkLoginState(); };   // This code loads the Facebook SDK asynchronously. This way, the page // is displayed as soon as possible, and the Facebook elements are added // later when they are available. (function(d, s, id) {  var js, fjs = d.getElementsByTagName(s)[0];   // If there is already a facebook-jssdk element, do nothing.  // It means the Facebook SDK is already available.  if (d.getElementById(id)) return;    // Create an element for the Facebook SDK and call it  // facebook-jssdk. Put the element into the variable  // js  js = d.createElement(s); js.id = id;    // This is the script to get the source for the SDK.  js.src = "//connect.facebook.net/en_US/sdk.js";    // Insert the script before the first element in the  // document  fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));     // This function is called when we KNOW the user is logged on. var loggedOn = function() {  setFacebookStatus("You're in"); }
  5. public/index.html 文件中 head 标签的主要用途是引用网页使用的所有库。将它替换为以下代码。
    <title>Using Facebook Sample App</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">   <!--  Use jQuery  --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"> </script>   <!-- Use the Bootstrap theme --> <link rel="stylesheet"  href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" /> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css" /> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"> </script>  <!--  Use the Angular library  --> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"> </script>   <!-- Use the application's scripts --> <script src="scripts/facebook.js"> </script> <script src="scripts/datamodel.js"> </script>
  6. 将 public/index.html 文件中 body 标签的内容替换为:
    <!-- Everything in the body is part of the myApp    Angular application --> <body ng-app="myApp"> <!--  Everything in this div has access to the scope variables    in facebookCtrl (defined in datamodel.js)  -->  <div ng-controller="facebookCtrl" id="facebookCtrl">     <!-- The Facebook login button. The scope attribute   identifies the permissions we want the user   to give us  -->   <fb:login-button scope="public_profile,email"    onlogin="checkLoginState();">   Login   </fb:login-button>    <h3>Status</h3>      <!-- When inside an Angular scope, such as facebookCtrl,   code encased in {{ }} is evaluated as JavaScript with   access to the scope variable ($scope). In this case,   it returns the value of $scope.fbStatus, which is   determined by setFacebookStatus().  -->   {{fbStatus}}  </div> </body>
  7. 运行应用程序,确保状态会根据您是否登录 Facebook 以及您是否向应用程序授权而发生变化。您可在 Facebook 中撤销应用程序权限,看看应用程序未授权时会发生什么。

第 5 步. 重定向到 HTTPS

通过未加密的通道(比如 HTTP)接受身份验证信息不是个好主意。现在,如果用户尝试通过 HTTP 登录,那么 Facebook 会拒绝该请求。

使用 Bluemix 和 MEAN 堆栈构建自助发表 Facebook 信息的应用程序,第 1 部分:使用 Facebook 作为...

如果应用程序自动将浏览器重定向到 HTTPS,这样会对用户更加友好。要添加此特性,打开 app.js 文件并将 app.use 函数调用替换为以下代码。如果现在有 app.get 函数调用,请删除它。请注意,与其他 JavaScript 文件相反,app.js 文件在服务器上运行。这是响应 HTTP 请求并向浏览器发出文件的代码。

app.get('/*', function(req, res) {   // If the forwarded protocol isn't HTTPS, send a redirection  if (req.headers["x-forwarded-proto"] != "https")   // Always redirect to /. This is a single-page application   // meaning users have only one URL they need to access   // directly. Any changes on that page to display information   // will be handled by client-side JavaScript   res.redirect("https://" + req.headers.host + "/");  else {  // Actually serve the request file      // Get the path. req.params[0] is the first wildcard in the   // file path. In the case, the file path in the get command   // is "/*", so it is everything after the initial slash.   //   // If there is nothing there, then the user just asked   // for the website, and we need to insert the index.html   // file.   var path = req.params[0] ? req.params[0] : 'index.html';      // Actually send the file from the public directory.   res.sendFile(path, {root: './public'});  }   });

查找请求字段

我最初不知道要使用的值是 req.headers["x-forwarded-proto"] 。为了找到此值,我首先将此行代码放在函数中:

console.log(Object.keys(req));

控制台中的结果(可在 Eclipse 中看到)为我提供了 req 对象中的字段列表。然后我查看了头部,看看它包含哪些字段:

console.log(Object.keys(req.headers));

需要检查 x-forwarded-proto 的原因是,Bluemix 应用程序始终会接收 HTTP。在互联网与 HTTPS 隧道终端的应用程序之间的路径上有一个 DataPower 设备。因此,您不能依靠该协议来区分 HTTP 与 HTTPS。

结束语

现在您可从 Facebook 登录一个浏览器,该浏览器正在运行 Bluemix 上的应用程序。这还不是很有趣,在本系列的下期 “ 将用户信息存储在服务器上 ” 中,您将学习如何使用这种登录方法将从 Facebook 获取的用户信息存储在服务器上。

正文到此结束
Loading...