Google App Engine 上的 Java Facebook 应用程序

在 Google App Engine 上创建、实现和部署一个 Facebook 应用程序

Facebook Platform 中缺少原生的 Java™ 支持,并不一定会对使用 Java 语言构建第三方 Facebook 应用程序造成阻碍。本文将展示如何创建一个 Facebook 应用程序,如何使用 Java 实现该应用程序,以及如何在 Google 的 App Engine 服务上部署该应用程序。

Mr. Joseph P. McCarthy, 软件开发人员, IBM

Joseph McCarthy 的照片Joseph McCarthy 是都柏林软件实验室的一名 Java 软件开发人员。他于 2002 年 7 月从 University of Limerick 毕业后就加入了 IBM。他获得计算机系统学士学位和计算机工程研究生毕业证。


developerWorks 投稿作者

2012 年 7 月 30 日

Facebook 启用于 2004 年 2 月,是世界上最大的社交网络,有 9 亿多名用户使用该网站与其朋友共享信息。Facebook Platform 启用于 2007 年 5 月,使第三方可以编写与 Facebook 集成的应用程序。该平台最初支持多种编程语言(其中包括 Java),但现在只提供适用于 JavaScript 和 PHP 的原生 SDK(还支持 iOS 和 Android 设备上的应用程序)。然而,自从 Facebook 停用 Java API 以来,一直由开源 RestFB 项目对 Java API 进行维护(参阅 参考资料)。

Google App Engine (GAE) 是一种平台即服务 (PaaS),它支持已注册的开发人员运行使用 Python、Java 或 Google 基础架构上的 Go 语言编写的应用程序。本文将向您展示如何注册一个 Facebook 应用程序,如何使用 Java 开发此应用程序,以及如何部署它,以便任何已登录的 Facebook 用户在 GAE 上随意使用它。(请注意,Google 在每个部署在 GAE 上的应用程序可以使用的资源上实施了每日限制。)

您将创建的简单应用程序会列出所有用户好友以及他们的 ID 和个人资料图片,外观类似于之前 Facebook 用户个人资料上的好友页面的外观。要开发此应用程序,您需要:

  • 一个 Facebook 帐户
  • 一个 Google 帐户
  • 安装了 GAE 插件的 Eclipse IDE(参阅 参考资料
  • 熟悉如何使用 Eclipse

应用程序的样例代码也可通过 下载 获得。

注册应用程序

第一步是在 Facebook 和 GAE 上注册应用程序。最好的做法是同时在两个平台上创建此应用程序,确保您输入的信息与所需的信息相匹配。

注册一个 Facebook 应用程序

登录到 Facebook,并在 https://developers.Facebook.com/appsand 中启动开发人员应用程序。(如果您是第一次启动它,则必须授予应用程序访问您的个人资料的权限,以便继续后面的步骤。)

单击 Apps 页面右上角的 Create New App 以显示 Create New App 对话框,如图 1 所示:

图 1. Facebook Create New App 对话框
Facebook Create New App 对话框的屏幕截图

为应用程序输入一个有效的名称和可用的命名空间。命名空间是一个将在 Facebook app URL 中使用的单字标识符。(在 图 1,我输入 My Old Friends 作为应用程序名称,并输入 myoldfriends 作为命名空间。)由 Heroku 提供的免费托管选项保持未选中,并单击 Continue。在下一个对话框中键入 CAPTCHA 代码,并单击 Submit 以弹出新应用程序的基本设置对话框,如图 2 所示:

图 2. Facebook 应用程序的基本设置对话框
Facebook 应用程序的基本设置对话框的屏幕截图

注意屏幕顶端的 App ID 和 App 密钥(隐藏在 图 2 中)。Facebook 使用它们来识别应用程序。请保持这些信息的私有性,不要让其他开发人员使用它们,因为这些用户会在您不知道的情况下恶意使用这些信息。

App Domains 字段中键入应用程序域。这必须是您将在 GAE 开发人员网站上为应用程序注册的 GAE 域,所以必须以 .appspot.com 结尾。例如,在 图 2,我输入了 myoldfacebookfriends.appspot.com。由于此域不再可用,所以您必须使用另一个域。只需确保它与您 注册 GAE 应用程序 时使用的应用程序标识符相匹配即可。

在 Select how your app integrates with Facebook 下,请选择 Website with Facebook Login 并输入一个网站 URL,此 URL 由您在 App Domains 字段中输入的 GAE 域组成,并以 http:// 为前缀。(在 图 2 中,我输入了 http://myoldfacebookfriends.appspot.com。)

画布 URL

您在基本设置中为应用程序指定的画布 URL 会告诉 Facebook 从何处获取页面代码来填充 Canvas Page,这是您运行应用程序的 Facebook 中的一个空白画布。当用户请求 Canvas Page 时,Facebook 会在该页面上的 <iframe> 中加载 Canvas URL。

选择 App on Facebook 并输入即将在其中运行应用程序的 servlet 的画布 URL。对于该应用程序,画布 URL 要以问号结尾,这表示应用程序的参数将通过请求 URL 传递给应用程序。安全的画布 URL 与画布 URL 相同,只是使用 https 替代了 http。此外,URL 结尾处的问号非常重要。(我应用程序的 servlet 的 URL 是 http://myoldfacebookfriends.appspot.com/myoldfacebookfriends,所以在 图 2 中,我输入 http://myoldfacebookfriends.appspot.com/myoldfacebookfriends? 作为画布 URL,并输入 https://myoldfacebookfriends.appspot.com/myoldfacebookfriends? 作为安全画布 URL。)

这是您在 Facebook 中设置应用程序所需要做的事情,但是配置其他一些设置(例如应用程序图标和类别)来修改向用户呈现应用程序的方式是一个很好的想法。

在 GAE 上注册应用程序

现在您已在 Facebook 上注册了该应用程序,您接下来要在 GAE 上注册该应用程序。

登录到 GAE 上的应用程序页面 (https://appengine.google.com/) 并单击 Create Application。在 Application Identifier 下,输入您在 Facebook 应用程序基本设置中使用的相同应用程序域名。(appspot.com 部分是为您提供的。)您可以使用任何您想使用的应用程序标题,搜索已注册应用程序的时候会用到它。其他选项采用其默认值。

图 3. GAE 的 Create an Application 对话框
GAE 的 Create an Application 对话框的屏幕截图

单击 Create Application 完成 GAE 注册过程。


开发应用程序

在 Eclipse 中,通过单击 File > New > Web Application Project 或者 Google Service and Deployment Tools 菜单下的 New Web Application Project 按钮来创建一个新的 GAE 项目,输入项目名称和包名称。取消选定 Use Google Web Toolkit。下载 RestFB JAR 文件(参阅 参考资料)并将其添加到项目的 WEB-INF/lib 文件夹。

将应用程序的 servlet 定义添加到项目的 web.xml 文件中。清单 1 显示了我使用的定义:

清单 1. Servlet 定义
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<servlet>
<servlet-name>myoldFacebookfriendsServlet</servlet-name>
<servlet-class>com.Facebook.friends.MyOldFacebookFriendsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myoldFacebookfriendsServlet</servlet-name>
<url-pattern>/myoldFacebookfriends</url-pattern>
</servlet-mapping>
</web-app>

注意,<url-pattern> 与 Facebook 应用程序基本设置对话框中的画布 URL 相同,但是没有问号。

从 2011 年 10 月开始,要求 Facebook 应用程序启用 Secure Sockets Layer (SSL);但在 GAE 上,默认情况下是禁用此功能的。要启用该功能,请将下列行添加至 appengine-web.xml 中:

<ssl-enabled>true</ssl-enabled>

Facebook 签名请求

使用 HTTP POST 方法,Facebook 会将签名请求发送至应用程序 servlet,以创建应用程序的内容。该请求包含一个 64 位编码的负载,在其他元数据中,该负载包含用于当前用户的应用程序的 OAuth 令牌。仅在用户授权应用程序访问其个人资料时,才能在请求的负载中包含该令牌。您需要将其转换成一个 Java 对象,以便应用程序可以使用它。

清单 2 显示了签名请求的 Java 对象的源代码。为了清楚起见,我省略了所有合适的 get 和 set 方法;这些方法被包含在源代码下载中(参阅 下载)。

清单 2. 签名请求对象
import org.apache.commons.codec.binary.Base64;
import org.codehaus.jackson.map.ObjectMapper;

public class FacebookSignedRequest {

   private String algorithm;
   private Long expires;
   private Long issued_at;
   private String oauth_token;
   private Long user_id;
   private FacebookSignedRequestUser user;

   public static class FacebookSignedRequestUser {

      private String country;
      private String locale;
      private FacebookSignedRequestUserAge age;

      public static class FacebookSignedRequestUserAge {
         private int min;
         private int max;

      }
      
   }
   
}

您可以使用 Apache Commons Codec 库中的 Base64 对负载进行解码。解码的负载位于 JavaScript Object Notation (JSON) 中,并且可以使用 Jackson JSON 处理器将其转换成一个 Java 对象。下载其 JAR 文件并将这些文件添加到项目中(参阅 参考资料)。将清单 3 中显示的静态 Helper 方法添加至 FacebookSignedRequest 类来创建该对象:

清单 3. 编码和解码负载的 Helper 方法
public static FacebookSignedRequest getSignedRequest(String signedRequest) 
      throws Exception {
   
   String payload = signedRequest.split("[.]", 2)[1];
   payload = payload.replace("-", "+").replace("_", "/").trim();
   String jsonString = new String(Base64.decodeBase64(payload.getBytes()));
   return new ObjectMapper().readValue(jsonString, 
                              FacebookSignedRequest.class);
   
}

创建 servlet

现在您可能开始编码将在 servlet 中运行的应用程序。使用与 web.xml 中的 <servlet-class> 定义相同的签名创建一个新类。首先,您需要使用 SignedRequest 类从负载中提取 OAuth 令牌,如清单 4 所示。

清单 4. 提取 OAuth 令牌
String signedRequest = (String) request.getParameter("signed_request");
FacebookSignedRequest FacebookSR = null;
try {
   FacebookSR = FacebookSignedRequest.getSignedRequest(signedRequest);
} catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
}
String oauthToken = FacebookSR.getOauth_token();

如果 oauthToken 对象为 null,那么用户没有授予访问该应用程序的权限,必须重定向至身份验证 URL。此 URL 是在所有应用程序间使用的标准 URL,它采用了以下格式:

https://www.Facebook.com/dialog/oauth?client_id=API KEY&redirect_uri=
   https://apps.Facebook.com/Application Namespace/&scope=Permissions

身份验证 URL 中的 API KEYApplication Namespace 是应用程序 Facebook 基本设置对话框中所显示的内容。Permissions 是您应用程序所需的权限列表。所有应用程序在默认情况下均拥有基本权限,您不必额外添加其他权限。(参阅 参考资料,获取可用权限的完整列表的链接。)

您可以在 Facebook 开发人员应用程序的 Settings > Auth Dialog 页面中定制该页面。通常,您会使用 servlet HttpServletRequest.sendRedirect() 方法,但是因为应用程序将在 apps.Facebook.com 域上的 <iframe> 中运行,所以一个简短的 JavaScript 代码段(如清单 5 所示)将浏览器窗口的位置改为应用程序 URL:

清单 5. 重定向至应用程序 URL 的 JavaScript 代码
PrintWriter writer = response.getWriter();
if(oauthToken == null) {
   
     response.setContentType("text/html");
     String authURL = "https://www.Facebook.com/dialog/oauth?client_id="
       + API_KEY
       + "&redirect_uri=https://apps.Facebook.com/myoldfriends/&scope=";
     writer.print("<script> top.location.href='" + authURL + "'</script>");
     writer.close();

}

Facebook Graph API

Graph API 使您可以读取 Facebook 社交图的属性和连接。您可以读取特定的字段,获取任何对象的图片,内省元数据的对象,并获取有关更改的实时更新。

凭借有效的 OAuth 令牌,您可以从 RestFB 库中创建 DefaultFacebookClient,并使用它通过 fetchConnection() 调用来检索 Facebook Graph API 中的数据。然后导航至 Facebook 上的 Graph Explorer,网址为 https://developers.Facebook.com/tools/explorer。在右上角的下拉菜单中选择您正在开发的应用程序,并单击 Get access token 来授权访问。然后单击 Connections 标题下的各种链接来查看结果。

要获得用户的好友,请单击好友 链接并查看结果。请注意,浏览器中的 URL 的值是 user id/friends。函数调用中的连接参数通常会使用 Graph Explorer 中相同的值。但是因为应用程序使用了已登录用户的数据,所以您可以使用 me 来代替用户 ID,让该值变成 me/friends。此调用会返回原始 Connection 类型,并且由于类的类型为 User,所以您需要将其作为一个参数添加。最后的调用是:

Connection<User> myFriends = FacebookClient.fetchConnection("me/friends", User.class);

fetchConnection() 方法调用的结果是保存在 Connection 类中 List 对象的 List 中。Connection 类实现了 Iterable 接口,所以您可以使用增强的 for 循环获取 List 中的每个 List 对象:

for (List<User> myFriendsList : myFriends)

在每个循环迭代中,myFriendsList 对象都包含该返回数据页面的当前用户列表。可以使用该列表中的每个 User 对象来创建 servlet 将创建的用户表的行。虽然能从 User 对象中检索用户 ID 和名称,但是无法从中检索个人资料图片。不过,Facebook 提供了一个 URL 来检索所有用户的个人资料图片:https://graph.facebook.com/User ID/picture。因此,个人资料图片的 URL 是通过使用 User 对象中的用户 ID 代替 URL 中的用户 ID 来创建的。使用和以前一样的 PrintWriter 对象,将一个带有标题行的表格写入画布:

writer.print("<table><tr><th>Photo</th><th>Name</th><th>Id</th></tr>");

如刚才所述,在整个 User 对象列表上进行迭代,接着使用每个 User 对象的实例变量为该表格编写一个新行:

for (List<User> myFriendsList : myFriends) {

   for(User user: myFriendsList)
      writer.print("<tr><td>" + 
         "<img src=\"https://graph.facebook.com/" + user.getId() + "/picture\"/>" +
         "</td><td>" + user.getName() +"</td><td>" + user.getId() + "</td></tr>");

}

最后,关闭 <table> 标签,并关闭 PrintWriter 对象,完成此 servlet:

writer.print("</table>");
writer.close();

清单 7 显示最终的 servlet doPost() 方法:

清单 7. doPost() 方法
public void doPost(HttpServletRequest request, HttpServletResponse response) 
  throws ServletException, IOException {
  
  String signedRequest = (String) request.getParameter("signed_request");
  FacebookSignedRequest facebookSR = null;
  try {
    facebookSR = FacebookSignedRequest.getSignedRequest(signedRequest);
  } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
  String oauthToken = facebookSR.getOauth_token();
  PrintWriter writer = response.getWriter();
  if(oauthToken == null) {
    
    response.setContentType("text/html");
    String authURL = "https://www.facebook.com/dialog/oauth?client_id="
      + Constants.API_KEY + 
      "&redirect_uri=https://apps.facebook.com/myoldfriends/&scope=";
    writer.print("<script> top.location.href='"  + authURL + "'</script>");
    writer.close();

  }
  else {
    
    FacebookClient facebookClient = new DefaultFacebookClient(oauthToken);
    Connection<User> myFriends = facebookClient.fetchConnection("me/friends", 
                                                User.class);
    writer.print("<table><tr><th>Photo</th><th>Name</th><th>Id</th></tr>");
    for (List<User> myFriendsList : myFriends) {

      for(User user: myFriendsList)
        writer.print("<tr><td><img src=\"https://graph.facebook.com/" + 
                 user.getId() + "/picture\"/></td><td>" + user.getName() +
                 "</td><td>" + user.getId() + "</td></tr>");

    }
    writer.print("</table>");
    writer.close();
    
  }

}

部署应用程序

创建了 servlet 后,就可以准备部署应用程序。单击 Eclipse 中的 Google 图标,并选择 Deploy to App Engine。编译应用程序后,将它上载至服务器,您(和其他任何一位 Facebook 用户)可以在 Facebook 的 http://apps.facebook.com/APP ID/ 中安装该应用程序并查看结果。


结束语

本文展示了如何使用 Java 注册、实现和部署 Facebook 应用程序,并将它托管在 Google App Engine 服务上。既然您已经非常熟悉基础知识,建议您尝试一些变化。

并非直接向页码编写 HTML 来获得内容,而是通过对 JavaServer Pages (JSP) 页面使用标准 RequestDispatcher.forward() 调用来实现一个更加传统的 Model-View-Controller (MVC) 方法。

您可以尝试创建一个对 Graph API 提供的数据使用额外权限的应用程序。您可以通过逐个添加至 scope 请求参数,将应用程序所需权限的列表传递至身份授权 URL。RestFB 提供了一个 helper 方法 ( StringUtils.join() ) 来正确创建列表。该方法只使用一个 String 阵列作为参数;阵列中的每个条目均是一个权限名称。

最后,您可以尝试使用 Facebook-java-api Google Code 项目(Facebook API 的备用实现)而不使用 RestFB 来重新创建样例应用程序(参阅 参考资料)。


下载

描述名字大小
本文的样例代码j-fb-gaecode.zip5KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=828176
ArticleTitle=Google App Engine 上的 Java Facebook 应用程序
publish-date=07302012