有时用户希望当他们不在线时,服务器会代表他们向 Facebook 发表帖子。例如,业务页面的所有者可能希望在某款产品的库存不多时发布公告,鼓励顾客在还有货时尽快购买。或者一个人可能希望他的时间表以随机的间隔发布消息。
可以编写一个服务器应用来实现此目的,但这么做并不容易。在这个 3 教程系列文章中,我将展示如何使用 IBM Bluemix 作为云提供商来实现此目的。本系列还会介绍 MEAN 堆栈所有 4 个组件的基本知识。为了演示此功能,我将展示如何构建一个应用程序,在随机的时间代表用户发表笑话。
备注:Bluemix 包含一个 Single Sign On 服务,该服务可从 Facebook 以及其他来源接收登录信息。但是,我在本教程中不会使用它。此处说明的应用程序类型与 Facebook 具有紧密的联系,所以让用户使用其他帐户登录没什么帮助。
获取代码
有两个 API 可供 Facebook 应用程序开发人员使用:
这个 Facebook 标准 API 假设用户已通过浏览器登录到应用程序,并通过在浏览器中执行的 JavaScript 代码来完成大部分操作。
从安全的角度讲,这是一种出色的机制,因为它让 Facebook 承载的库能够直接联系用户来要求获得权限。但是它不适合本教程的用途。我们需要在用户未登录时系统能代表用户执行操作。
还可以直接从服务器使用的另一个 API。它使用基于 HTTP 请求的 REST 架构,这使得它更难使用,但是可以确保您能从任何现代服务器编程中环境获得它。这是本系列文章中使用的主要接口。
首先登录 Bluemix 并创建一个 Node.js 应用程序。请参阅 developerWorks 上的IBM Bluemix 页面 获取帮助。
本教程假设您正在修改默认的 Node.js 入门应用程序。
按照以下步骤在 Facebook 中创建应用程序:
<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>
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>
有时 Bluemix 和 Eclipse 不同步,而且在尝试上传和运行应用程序时会出现服务器错误。
server
。 下一步是让用户通过 Facebook 登录您的应用程序。
https://<application name>.mybluemix.net/index.html一般而言,加密对应用程序的访问是个不错的想法。通过仅指定
https
,您可确保您的应用程序不支持未加密的登录。 下一步是让用户使用客户端 JavaScript 接口,从应用程序的网页登录 Facebook。这也是您开始使用一些库的地方,比如 Angular 和 Bootstrap。我们对默认应用程序进行了太多的更改,无法一一列出,所以我在这里提供了修改后的代码并将解释放在注释中。
// 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; }); };
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"); }
<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>
<!-- 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>
通过未加密的通道(比如 HTTP)接受身份验证信息不是个好主意。现在,如果用户尝试通过 HTTP 登录,那么 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 获取的用户信息存储在服务器上。