本文假设您已经下载了 Project Zero 并或者已经完成了 介绍性教程 的学习或者自己编写过简单的应用程序。而且,您还应该阅读在本文 参考资料 部分给出的有关 Flickr 的 REST API 的概览。该概览包括了如何获得 Flickr API 键的指令,这些都是测试本文中所提供的代码所必需的。
Flickr 照片共享服务是当今最为流行的 Web 应用程序之一,它包括了一个公共 API,让开发人员可使用 HTTP 请求访问照片数据。Flickr 的 API 是 RESTful 的而且十分容易理解,但若想进行请求和读取响应,还需要执行一些乏味的步骤。本文展示了如何去除这些乏味的步骤并让 Zero 和 Flickr 间的通信简单到只需几行代码。本文还探讨了 Project Zero 几个有趣的扩展点。
读写照片数据的 Flickr API 的基础是以 HTTP 发送的消息和以 XML 或 JSON 为格式的数据结构。本文使用的是 JSON,因为它的数据结构(简单映射和列表)更容易从 Groovy 的角度处理。在本节,将会介绍如何使用 Zero 的全局上下文和 Groovy 的脚本为 Flickr 的 API 创建一些简单的包装器方法。
要完成我们的 Flickr API “Zero 化”处理,需要创建两个 Zero 项目:一个针对用来与 Flickr 交互的 Groovy 代码,一个使用这些 Groovy 代码完成某些任务。第一个项目名为 zero.services.flickr,第二个项目名为 flickr.test。如果想要跳过这个创建步骤,可以直接从 参考资料 部分下载完整的示例项目。如果想要亲自编写代码,则可以使用清单 1 所示的 Zero 命令行创建这两个简单的项目:
清单 1. 创建测试项目
$ zero create zero.services.flickr
$ zero create flickr.test
|
为 flickr.test 编辑 Ivy 文件并在 zero.services.flickr 上添加一个依赖项。打开 /config/ivy.xml 并添加如清单 2 所示的 XML 代码行:
清单 2. 添加 Ivy 依赖项
<dependency name="zero.services.flickr" org="zero" rev="1.0+"/>
|
zero.services.flickr 项目并不需要额外的库或依赖项,因为 Flickr API 构建在 HTTP 和 JSON 基础上;我们将使用 Zero Core 所包含的 HTTP 和 JSON 库与 Flickr 通信并创建到实际图像文件的 URL。
每个 Flickr 请求都需要一个方法名和一组参数集(参数集也可能为空)。响应作为 JSON 对象返回,JSON 对象除了所请求的数据外还具有状态属性(设为 ok 或 fail)。您可以充分利用这样一个事实,即 JSON 对象在 Groovy 中表示为简单的映射和列表并创建一个十分简单的 API 来发送请求和读取响应。让我们开始一个用例:您可能想让您的用户能用三四行代码即可发出 Flickr 请求,如清单 3 所示:
清单 3. “Zero 化” 的 Flickr API 原型 #1
def method = "flickr.photosets.getPhotos";
def params = [
photoset_id: 1234567890,
per_page: 25
];
def response = invokeMethod("flickr.groovy", "flickr");
|
该代码非常棒,但还可以通过利用 Zero 的全局上下文加以改进。将请求和响应数据存储在全局上下文中让开发人员可以更为容易地跨脚本或文件划分进行请求和响应处理;例如,一个服务器端脚本可能会向一个或多个服务发出 API 调用,而同时响应数据则由客户端的模板呈现。清单 4 显示了如何相应处理此用例:
清单 4. “Zero 化” 的 Flickr API 原型 #2
request.flickr.method = "flickr.photosets.getPhotos";
request.flickr.params = [
photoset_id: 1234567890,
per_page: 25
];
invokeMethod("flickr.groovy", "flickr"); // uses GC for I/O
def response = request.flickr.response[];
|
设计好之后,就可以开始编写代码了。在 zero.services.flickr 项目内,在 /app/scripts/flickr.groovy 下创建 Groovy 脚本。第一个要添加到此文件中的方法是清单 3 和 4 中的 flickr() 方法。要实现此方法,需要注意如下三个方面:
- Flickr API 调用所需的 URI 格式。
- Flickr API 键的值。
- 外向(outbound) HTTP 请求的 Zero Connection API。
有关 Flickr API 调用所需的 URI 格式的相关内容在 Flickr API 文档(见 参考资料)可以找到;其本质是为用户所提供的每个请求参数创建一个含查询参数的 URI。此外,还必须包含 API 键,这意味着为了提供此 API 键,必须要有一个约定(以免用户在脚本中硬编码其 API 键)。清单 5 显示了构建这个请求 URI 所需要的代码:
清单 5. 构建 Flickr 请求 URI
def uri = "http://api.flickr.com/services/rest/?format=json&nojsoncallback=1";
uri += "&api_key=${config.flickr.key[]}";
uri += "&method=${request.flickr.method[]}";
request.flickr.params[].each() {
uri += "&${it.key}=${it.value}";
}
|
请注意清单 5 中已经硬编码了基本的 Flickr URI 并使用了 JSON 数据结构 — 这些不应更改。还有,这个 API 键是从一个名为 config.flickr.key 的全局上下文属性中读取的;我们将要求用户在其应用程序配置文件中设置这个值以鼓励好的编程实践。清单 5 代码中最后一点需要注意的是 Groovy 的语法技巧是如何简化将 JSON 对象(映射)转变为有效 URI 的过程的。通过使用嵌入式变量和 each() 方法来在此映射上进行迭代,用五行非常易读的代码,我们就创建了一个相当复杂的 URI。
一旦有了 URI,只需使用 Zero 的 Connection API 发送请求即可。幸运的是,这个 API 有很多简便的方法来调用 RESTful 服务。清单 6 中的代码做了这样一个 HTTP GET 请求并以 JSON 解析了相应的响应:
清单 6. 处理 Flickr 请求
def response = Connection.doGET(uri);
def json = response.getResponseBodyAsString();
request.flickr.response = JSONObject.parse(json);
|
对于此项目,我们假设所有操作都是 GET(读取);所提供的代码可以很容易地扩展到 POST(写入),但这超出了本文的范围。
整个 flickr() 方法是由清单 5 和 6 中的代码组成的,也可以在本文所附的示例项目下载中得到。如果在将两个清单合并成一个可用函数方面遇到任何麻烦,可以参考下载中的 flickr.groovy 文件。
调用方法只是用户将 Flickr 数据合并到应用程序中的所有工作中的一部分。一旦有了一个或更多照片的元数据,用户将需要创建到由 Flickr 用户共享的这个实际图像文件的 URL。图像文件 URL 不包括在 JSON 响应数据中 — 必须用 Flickr 提供的格式化指令将它们合并(参见 参考资料) ;因为这些 URL 通常很长并且包括很多变量,为了能从 JSON 响应中构建这些 URL,需要有一些简便的方法。
清单 7 显示了两个简单方法,用来获取 Flickr 的 photo 数据结构之一(为所有返回照片元数据的方法所用)并为它的图像文件创建 URL。受托管的照片实际上都有几个图像文件,我们的代码说明了这一点;用户可以将 Flickr 返回的 photo 数据传递给我们的 thumbnail() 和 original() 方法以查看缩略图和原始图像。清单 7 中的代码包括在与 flickr() (/app/scripts/flickr.groovy) 相同的文件里:
清单 7. 配置 Flickr API 键
def original(photo)
{
def server = "http://farm${photo.farm}.static.flickr.com/${photo.server}";
def name = "${photo.id}_${photo.originalsecret}_o.${photo.originalformat}";
return "${server}/${name}";
}
def thumbnail(photo)
{
def server = "http://farm${photo.farm}.static.flickr.com/${photo.server}";
def name = "${photo.id}_${photo.secret}_t.jpg";
return "${server}/${name}";
}
|
代码到目前为止很有效,但我们用户代码中的 invokeMethod() 的使用还不够完美。invokeMethod() 方法对于所有运行于 Zero 平台上的 Groovy 脚本都可用,它还提供了一种迅捷的方法来调用另一脚本中的代码;对于只需重复使用一两次的代码而言,这种做法还可以,但对于像这样的一个库来说,调用可能更实际些。如果能更新清单 4 中的代码来直接调用 flickr() 方法,就最好不过了,如清单 8 所示:
清单 8. “Zero 化” 的 Flickr API 原型 #3
request.flickr.method = "flickr.photosets.getPhotos";
request.flickr.params = [
photoset_id: 1234567890,
per_page: 25
];
flickr();
def response = request.flickr.response[];
|
Zero Core 已经为常用的几个 API 实现了这种功能,包括 render()、listFiles() 和 getRequestedUri()。这些常用的方法就称为绑定。此外,Zero Core 还允许您用自己的方法扩展默认的 Groovy 绑定,以便能更容易地从应用程序的任何位置对其进行调用。本节将介绍如何为 Flickr 方法创建 Groovy 绑定并提高它们的可用性。
要想添加定制的 Groovy 绑定,需要编写一个类,该类实现 zero.core.groovysupport.bindings.BindingHandler
接口,然后再在应用程序的 zero.config 文件注册该类。BindingsHandler 接口只有一个方法 —
addVariables()
— 用来将绑定名(比如 flickr())映射到它们的实际实现。清单 9 显示了如何在 zero.config 中配置 BindingsHandler 类:
清单 9. 向 Zero Core 添加 Groovy 绑定
[/config/bindings/.groovy[]]
my.bindings.handler.ClassName
|
如果所提供的是方法绑定(正如我们现在所做的),那么通过在 zero.core.groovysupport.bindings.InvokeBindings 中扩展类可以简化很多工作 — 这种做法能将方法调用转给 invokeMethod(),由它负责处理 Groovy 代码的执行。有了这个机制,就能够享用这些更巧妙的方法调用的所有益处,且不必自已加载和执行 Groovy 脚本。不管接口如何实现,这个配置文件将不会改变。
为了使这三个 Flickr 方法对于其他 Groovy 脚本编写器也方便使用,我们已经创建了一个名为 FlickrBindings 的类,它将方法名映射给对 invokeMethod() 的调用,包括用户提供的参数。清单 10 显示了此类的代码。请把清单 10 中的这个类加入到 zero.services.flickr 示例项目中:
清单 10.
FlickrBindings 类
package zero.services.flickr;
import java.io.FileNotFoundException;
import java.util.Map;
import org.codehaus.groovy.runtime.MethodClosure;
import zero.core.groovysupport.bindings.InvokeBindings;
import zero.json.java.JSONObject;
public class FlickrBindings extends InvokeBindings
{
private static final String _SCRIPT = "flickr.groovy";
private static final String _FLICKR = "flickr";
private static final String _ORIGINAL = "original";
private static final String _THUMBNAIL = "thumbnail";
public void addVariables(Map<String, Object> variables)
{
super.addVariables(variables);
variables.put(_FLICKR, new MethodClosure(this, _FLICKR));
variables.put(_ORIGINAL, new MethodClosure(this, _ORIGINAL));
variables.put(_THUMBNAIL, new MethodClosure(this, _THUMBNAIL));
}
public void flickr()
throws FileNotFoundException, NoSuchMethodException
{
invokeMethod(_SCRIPT, _FLICKR, null);
}
public String original(JSONObject photo)
throws FileNotFoundException, NoSuchMethodException
{
Object gstring = invokeMethod(_SCRIPT, _ORIGINAL, new Object[]{ photo });
return gstring.toString();
}
public String thumbnail(JSONObject photo)
throws FileNotFoundException, NoSuchMethodException
{
Object gstring = invokeMethod(_SCRIPT, _THUMBNAIL, new Object[]{ photo });
return gstring.toString();
}
}
|
此类中的如下两项需要注意。第一个是为了将方法名映射到方法调用而使用了 Groovy 运行时的 MethodClosure 类; 此类所要实现的全部功能就是在调用给定对象上指定的方法并将结果返回给调用的脚本。第二点要注意的是返回字符串的 Groovy 方法也可能会返回 Groovy Strings(或 "GStrings"),它们是包含嵌入式变量的一些字符串;我们在 original() 和 thumbnail() 方法中遇到过这种情况。为了防止在用户想要字符串,得到的却是 GString 的情况下抛出异常,在传递方法返回值之前,我们使用了 toString() 方法。调用 toString() 就确保了所有 GString 的变量均被解析而且所创建的是所想要的字符串。
类完成后,剩下所要做的就是通过 zero.config 注册它。打开 zero.services.flickr 项目中的 /config/zero.config 文件并加入清单 11 中显示的语句:
清单 11. 向 Zero Core 添加 Groovy 绑定
[/config/bindings/.groovy[]]
zero.services.flickr.FlickrBindings
|
任何将 zero.services.flickr 作为依赖项加以包含的应用程序现在都能从其任意的 Groovy 脚本中调用这些 Flickr 方法。在一下节中,将实际应用这些方法来创建一个可在您自已站点内使用的、基于 Flickr 的小部件。
至此,我们已经编写了很多代码,但还没有真正地将这些代码投入测试,亦无从知道这些代码是否真的可以简化基于 Flickr 的 服务和小部件的编写。现在,我们将使用这些代码去创建一个很常见的用户界面组件:照片拼接。图 1 显示了我们想要创建的这种照片拼接的一个屏幕快照。您可能已经在 blog 和其他关注 Web 2.0 技术或社会性网络的网站中看到过类似的照片集。通过使用在前两节中编写的 Groovy 库,仅仅用几行代码就能创建这个小部件。
图1. 照片拼接的屏幕快照
创建如图 1 所示的照片拼接最简单的方法就是使用一个查找照片集的 Groovy 模板,然后使用由 Flickr 返回的照片集构建一个 HTML 表。更具体地说,就是使用 flickr() 方法查找照片记录,使用 thumbnail() 方法构建照片拼接,使用 original() 方法创建这个拼接到 Flickr 原始图像的超级链接。清单 12 显示了构建拼接表所需的 Groovy 代码;可以把它复制到一个更大的模板文件中,作为应用程序的完整用户界面的一部分。如果想立即测试一下这个小部件,它已经包括在 flickr.test 示例项目中 /public/index.gt 下。只要运行这个应用程序并用所喜欢的浏览器访问 http://localhost:8080 就可以看到这个小部件的运行。
清单 12. 配置 Flickr API 键
<table border='0' cellpadding='0' cellspacing='10'>
<%
def set = "72157602828759420"; // flickr photo set ID
def length = 4; // size of the photo display box
for (page in 1..length) {
//
// set name-value pairs as specified by flickr API
//
request.flickr.method = "flickr.photosets.getPhotos";
request.flickr.params = [
photoset_id: set,
per_page: length,
page: page,
extras: "original_format"
];
flickr(); // send request to flickr
def response = request.flickr.response[];
println "<tr>";
response.photoset.photo.each() { // build the photo display box
println "<td>";
println "<a href='${original(it)}'><img border='0' src='${thumbnail(it)}'></a>";
println "</td>";
}
println "</tr>";
}
%>
</table>
|
清单 12 中的头两行代码分别提供了 Flickr 照片集 ID 以及拼贴图框的大小;可以改变这些值,以决定所显示的是何照片以及这个框有多大(如果您手头没有可用于测试的照片集,就可以使用清单 12 中的这个 Project Zero 团队的照片集)。另外一个值得注意的地方就是您不会同时得到所有的照片数据;照片数据在页面中读取,这样就有一些照片已被呈现,其他照片则尚在显露的过程中。如果允许在同一时间得到所有照片,并且随后创建表单元格,那么在拼贴图最终弹出前,加载时的初始等待时间将会比较长。
在自己运行这个代码前,需要在应用程序中的 zero.config 文件中设定 Flickr API 键。本文开始的部分就曾指导您参考 Flickr 站点来获得自己的 API 键,希望您现在已经有了 API 键。清单 13 显示了如何使用您的个人 API 键设置前面章节讨论过的 config.flickr.key 属性:
清单 13. 配置 Flickr API 键
[/config/flickr]
key=1234567890
|
有了 API 键,就可以使用我们的定制 Groovy 绑定自由地读写 Flickr 数据。
Flickr 为那些想要将站点元素合并到自己的应用程序中的开发人员提供了一种非常有用、灵活的 API ,若能使用 Groovy 脚本编写和 Project Zero 平台会让事情更加简单。此外,本文中所介绍的这些技巧也可以适用到其他综合了社会性和数据分享的热门站点(比如 YouTube、Blogger 等)。只要 Web 站点能继续用 RESTful API 和可移植的数据格式公开其共享数据,您就总是能够将它们合并到 Zero 编程模型中。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| 具有 Flickr 集成/测试文件的示例应用程序 | wa-pz-flickrapi.zip | 8.4KB | HTTP |
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
-
了解有关 Flickr 的 RESTful API 并获得 Flickr API 键。
-
了解有关 Flickr 的 URL 模式 以便创建到所托管的图像文件的链接。
- 加入
Project Zero 社区 以了解该项目的方方面面。
-
浏览 developerWorks Web 开发专区 获得各种工具、代码和资源以立即开始开发 Web 2.0 应用程序。
-
developerWorks Ajax 资源中心 富含面向所有技能级别读者的信息,可以帮助您将 Ajax 构建到应用程序并显著改进用户的 Web 体验。
获得产品和技术
-
下载 Project Zero 并开始应用本文中所给出的这些最佳实践。
讨论
-
参与 projectzero.org 论坛。
