跳转到主要内容

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

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

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

  • 关闭 [x]

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

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

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

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

  • 关闭 [x]

利用 Project Zero 和 REST 设计原理创建相册应用程序

以 Zero 作为 RIA 平台

Dan Jemiolo, 咨询软件工程师, EMC
Dan Jemiolo 是 IBM 位于美国北卡罗来纳州 Research Triangle Park 的 Project Zero 团队的一名咨询软件工程师。目前主要负责 Zero 平台的可重用组件及其服务目录的工作。他的工作包括 Apache Muse 2.0 的设计和开发,也曾参加过 OASIS Web 服务标准组织。三年前他从 Rensselaer Polytechnic Institute 获得了计算机科学的理学硕士学位,之后加入了 IBM。
(An IBM developerWorks Contributing Author)

简介: Project Zero 的重要目标之一是简化 RIA(rich Internet application)的开发。Flickr 照片共享服务就是这类应用程序的一个很好的例子。Flickr 使用 REST 原理、Asynchronous JavaScript + XML(Ajax)技术和动态脚本语言设计,所提供的服务不仅用户友好,而且是可伸缩和可扩展的。由于 Flickr 具有其他 RIA 作者想要的许多特性,用 Zero 重新创建这类应用程序将是很好的一种作为 RIA 平台验证 Zero 的方式。在本文中,了解如何结合现有的 Zero 组件来创建照片共享服务,此服务也支持 Flickr 提供的许多功能。通过本文,您还会了解到有关 RESTful 设计、通过 HTTP 连接组件以及使用 JavaScript 实现 Zero 没有的功能的更多内容。

发布日期: 2007 年 11 月 16 日
级别: 中级
访问情况 : 1630 次浏览
评论: 


开始之前

本文假设您已经下载了 Project Zero 并且完成了 简明教程 的学习,或者曾经写过简单的应用程序。您应该熟悉 REST 的基本原理和不同类型的 HTTP 方法(GET、POST 等等)。有关 REST 的介绍,请参考 参考资料 部分。

照片共享应用程序设计简介

Yahoo! 的 Flickr 照片共享服务(参考 参考资料)是一个很好的 RIA 示例。由于 Flickr 具有其他 RIA 作者想要的许多特性,使用 Zero 重新创建这类应用程序,能够很好地证明 Zero 是一款开发 RIA 的优秀平台。幸运的是,我们无须从头创建整个照片共享服务 — Zero 平台包括许多 RIA 组件,例如,blog、评定系统、配置文件管理器等等,在构建您自己的应用程序时,所有这些组件均可重用。接下来的章节将讨论照片共享服务的本质,同时也会展示有了 Zero 中的这些组件后,这类应用程序的共同之处非常之多。

Project Zero 社区
请查阅 Project Zero 网站,了解 Project Zero 如何提供功能强大 但异常简单 的 Web 应用程序开发和执行平台。这个活跃的 社区 探讨项目开发、为开发人员提供帮助,而且非常乐意听取您的想法!

设计照片共享应用程序

创建照片共享服务初看上去非常复杂,但如果静下心来仔细想想,就会发现这类应用程序有很多共同之处,它们都具有两个容易理解的 Web 2.0 应用程序:blog 和文件共享。Flickr 本质上是一种 blog 服务,为其 blog 文章(blog post)提供了一种更加特定于域的视图。用户收藏夹中的每张照片都由一个 blog 文章表示,这些照片(blog 文章)再被组织成各个相册(blog 类别)并用描述性标记加以扩充。此外,Flickr 还允许用户访问普通用户界面之外的真正的图像文件,这就意味着图像存储是由一种与 blog 服务分离的高端文件共享服务处理的。Project Zero 的可选项库套件中既包括了 blog 与文件共享组件,而且二者还可以组合在一起提供类似的照片共享体验。

我们这个照片共享 UI 的实现包括用文件共享组件存储照片,和用 blog 组件对照片进行组织。当用户浏览文件共享或 blog 内容时,将会看到他们的照片和照片共享 UI 更加以照片为中心,而且用户友好性更好。奇特的 Ajax 和受 Flash 驱动的小部件都使得 Flickr UI 非常有吸引力,但是如何创建它们不属于本教程讨论的范围。本文介绍了驱动这些小部件行为所需的 RESTful 接口和逻辑。一个设计良好的照片共享服务就绪后,就可以在它上面放置任何类型的 UI 装饰了。

让我们先来仔细看一下 Zero 的 blog 和文件共享组件的设计,了解如何轻松地将它们应用到照片共享应用程序中。

将 RESTful 资源用作构建块

blog 和文件共享组件都由能使用 HTTP 方法处理的一个或多个资源类型组成。每个 HTTP 方法代表资源上的一个读或写操作,每个 HTTP URI 代表单个的资源实例。例如,针对 http://www.example.com/blogs/jsmith/posts/our-latest-vacation 的一个 HTTP GET 可能会返回用户 jsmith 的一个 blog 文章表示,标题为 Our Latest Vacation。同样地,针对 http://www.example.com/blogs/jsmith 的一个 HTTP POST 也可能被用来创建用户 jsmith 的 blog 中的一个新的 blog 文章。表 1 解释了 HTTP 方法和 URI 是如何用来与 Zero 的 blog 服务交互的,包括客户机和服务器之间交换的数据格式。用粗体表示的这些 URI 标记是变量:


表 1. Zero blog 组件的 REST 接口
URIHTTP 方法数据格式描述
/blogsPOSTJSON使用在请求体中发送的 JSON 表示创建一个新的 blog
/blogs/blog-name GETJSON检索给定名称的 blog 的资源定义
/blogs/blog-name PUTJSON更新给定名称的 blog 的资源定义
/blogs/blog-name DELETE删除给定名称的 blog,包括它的所有文章和注释
/blogs/blog-name/postsPOSTAtom基于在请求体中发送的 JSON 表示创建一个新的 blog 文章
/blogs/blog-name/posts/post-id GETAtom检索给定 ID 的 blog 文章的资源定义
/blogs/blog-name/posts/post-id PUTAtom更新给定 ID 的 blog 文章的整个资源定义
/blogs/blog-name/posts/post-id DELETE删除给定 ID 的 blog 文章

当通过 HTTP 添加身份验证和授权时,就会注意到此 API 提供了管理一个或多个 blog 所需的全部功能。此 API 可非常容易地被 Ajax 工具箱(例如 Dojo)调用,这就让开发人员能够将响应数据和 UI 小部件进行集成,而不需要页面刷新(有关 Dojo 工具箱的更多信息,请参阅 参考资料)。当然,此 API 也能通过普通 HTTP 客户机访问,比如 Apache 的 HttpClient(面向 Java™ 技术的)或 cURL 命令行工具(面向 C 的)(有关链接,请参阅 参考资料)。您甚至可以使用 Web 浏览器的地址栏调用此 API 的某些部分。可使用 JavaScript Object Notation(JSON)对 blog 进行调用和修改,而 blog 文章则可使用 Atom(有关链接,请参见 参考资料)进行处理。表 2 给出了用于文件共享组件的一个类似的 API,它存储公共文件和目录:


表 2. Zero 文件共享组件的 REST 接口
URIHTTP 方法数据格式描述
/file-shares/user-name POSTJSON使用用户文件共享的 JSON 表示向其中添加新文件或目录
/file-uploads/user-name POSTHTML 表单数据使用 HTML 表单向用户的文件共享中添加新文件或目录
/file-shares/user-name/path GETJSON检索用户文件共享中给定路径上的文件或目录
/file-shares/user-name/path PUTJSON更新用户文件共享中给定路径上的文件或目录
/file-shares/user-name/path DELETE删除用户文件共享中给定路径上的文件或目录

此文件共享 API 中值得注意的地方包括使用用户名来分离共享目录和使用双重方式来添加文件。每个用户都有自己的共享目录,这有利于使用用户照片共享帐号来确定存储图像文件的位置。在设计相册 UI 时利用 HTTP POST 添加文件的两个方法带来了很大的灵活性 — 既可以使用 HTML 表单来上传数据,也可以使用类似 Flickr Uploadr 工具的非浏览器客户机(有关链接,请参阅 参考资料)。

这些 API 准备好后,就可以用这两个组件上的一系列操作来设计我们的照片共享服务了。您无需知道任何有关组件实现的详情 — 所有与它们的交互都通过其 RESTful HTTP 接口来实现。下一步就是做一些实质性的决定,以便确定照片共享动作如何映射到 HTTP 请求。

将照片共享动作映射到 blog 和文件共享

现在可以开始设计应用程序了,方法是构建一个类似 表 1表 2 的表格,然后看看这些 blog 和文件共享组件已经涵盖了哪些功能以及需要编写什么代码。表 3 描述了需要支持的功能以及它们将如何映射到组件 API 调用:


表 3. 将照片共享动作映射到 blog 和文件共享
照片共享动作底层的动作
创建新照片共享帐号创建一个新的 blog 来对照片进行分类
为图像文件创建一个新的文件共享目录
创建新相册创建一个新的 blog 类别
向相册中添加照片将一个图像文件的副本上载到文件共享目录
用指向此图像文件的 <img/> 标记创建一个新的 blog 文章
检索所有照片检索此照片 blog 中的所有 blog 文章
检索整个相册检索给定类别中的所有 blog 文章
检索单个照片检索此照片的 blog 文章
更新单个照片,包括其相册更新此照片的 blog 文章和类别名
删除所有照片删除此照片 blog
可选地,可以连同图像文件删除此文件共享目录
删除整个相册删除此 blog 类别
删除类别中的 blog 文章
可选地,可以删除与这些 blog 文章相关的图像文件
删除单个照片删除此 blog 文章
可选地,可以删除与此 blog 文章相关的图像文件

表 3 说明了只有少数特性需要底层的多个操作,所以需要编写的胶合代码(glue code)数量将会最少。表 4 与表 3 基本相同,只不过描述性文字已经被需要使用的确切 HTTP 方法和 URI 所替代:


表 4. 将照片共享动作映射到 HTTP 请求
照片共享动作底层的动作
创建新的照片共享帐户HTTP POST 到 /blogs

HTTP POST 到 /file-shares/user-name
创建新相册HTTP POST 到 /blogs/blog-name/categories
向相册中添加照片HTTP POST 到 /files-shares/user-name

HTTP POST 到 /blogs/blog-name/posts
检索所有照片在 /blogs/blog-name 上进行 HTTP GET
检索整个相册在 /blogs/blog-name/categories/category-name 上进行 HTTP GET
检索单个照片在 /blogs/blog-name/post/post-id 上进行 HTTP GET
更新单个照片,包括其相册HTTP PUT 到 /blogs/blog-name/post/post-id
删除所有照片在 /blogs/blog-name 上进行 HTTP DELETE

在 /file-shares/user-name/photos 上进行 HTTP DELETE
删除整个相册在 /blogs/blog-name/categories/category-name 上进行 HTTP DELETE

在 /blogs/blog-name/posts/post-id 上进行 HTTP DELETE

在 /file-shares/user-name/photos/photo-file-name 上进行 HTTP DELETE
删除单个相片在 /blogs/blog-name/posts/post-id 上进行 HTTP DELETE

在 /file-shares/user-name/photos/photo-file-name 上进行 HTTP DELETE

HTTP 请求的所有特性都可以很容易地利用前面所提到的某一个 Ajax 或 HTTP 客户机库处理;多操作的特性将需要一些胶合代码,确保所有的 HTTP 请求均可成功完成(作为一个 “事务”)。在此处,尚未确立这个照片共享服务独立于 blog 或文件共享的任何特性,所以无需在 Zero 应用程序中创建任何新的 RESTful 资源。

实现照片共享应用程序

对照片共享应用程序背后的概念有了基本的了解之后,就可以着手进行实践了。本节假设您已经使用 Zero 的 Eclipse 插件或命令行工具创建了一个新的 Zero 应用程序,名为 photo-share。随后的指令会引用此命令行接口,但很容易就可从项目的上下文菜单中找到 Eclipse 对等物。

安装 blog 和文件共享组件

第一步是将此 blog 和文件共享组件添加到应用程序中。打开存在于 /config/ivy.xml 的应用程序的 Ivy 文件并添加如下代码:


清单 1. 将 blog 和文件共享组件作为依赖项添加
                
      
<dependency org="zero" name="zero.services.blog" rev="1.0.0+"/>
<dependency org="zero" name="zero.services.share" rev="1.0.0+"/>
      

一旦添加了 清单 1 中所示的 XML,就必须从命令行运行 zero resolve, 以完成这两个组件的安装。尽管它们实际的代码和工件还没有添加到项目中,但借助于 Zero 的依赖项解析(dependency resolution)机制,从您自己的代码对这些组件的引用会在运行时工作。

使用 JavaScript 将各块粘接起来

映射到单个 HTTP 请求的照片共享动作(请参阅 表 4)可以很容易地用 Dojo JavaScript 工具箱实现。Zero 包括了 Dojo 的最新版本(0.4.3),也可以通过向 Ivy 文件添加清单 2 中的 XML 从而将其添加到应用程序中:


清单 2. 添加 Dojo 依赖项
                
      
<dependency org="dojo" name="dojo" rev="0.4.3+"/>
      

通过 Dojo 可以很容易地将 HTML 表单数据转换成 HTTP 请求,而且这也是这些单一请求动作所需的全部操作。清单 3 中的代码显示了一个可用来输入新相册名称的 HTML 表单和一个可通过 HTTP POST 创建新相册的 JavaScript 函数。请注意从表单取得的数据是如何作为一个 JSON 对象直接添加到请求体中的;zero.services.blog 的实现需要一个 JSON 对象,而且根本无需修改这些数据即可处理请求。


清单 3. 用 HTML 和 JavaScript 创建一个新相册
                
    
<script type="text/javascript" src="/dojo.js">
</script>
    
<script>
dojo.require("dojo.io");
dojo.require("dojo.json");
dojo.require("dojo.widget.Form");
    
function createAlbum() 
{
    var form = dojo.widget.byId("CreateAlbumForm");
    var albumData = form.getValues();
        
    var blogName = getPhotoShareName();

    dojo.io.bind({
        url: '/resources/blogs/' + blogName + '/categories',
        method: 'POST',
        sync: false,
        mimetype: 'text/json', 
        contentType: 'text/json',
        postContent:  dojo.json.serialize(albumData),
        load: function(type, data) {
            alert("The new album was added successfully.");
        },
        error: function(type, err) {
            alert(dojo.errorToString(err)); 
        }
    });
}
    
function getPhotoShareName()
{
    return <%= _gc.get("/request/subject/remoteUser") + "-photos"; %>
}
    
</script>
    
...
    
<form dojoType="form" id="CreateAlbumForm" name="CreateAlbumForm">
  <table cellpadding="10" cellspacing="0" border="0" width="50%">
    <tr>
      <td><b>New Photo Album:</b></td>
      <td><input type="text" size="64" name="categoryid"/></td>
    </tr> 
  </table>
  <br/>
  <input type="button" onClick="createAlbum();" value="Submit" />
</form>
  

正如您所见,createAlbum() 函数直接从 HTML 表单(form.getValues())取得 JSON 数据,并将其作为一个到 dojo.io.bind() 的参数添加到 HTTP 请求。此请求是一个带 URI /blogs/blog-name/categories 的 HTTP POST,如 表 4 所示。请注意即使 HTML 告诉用户他们正在创建一个新相册,实际上,在底层,是创建了一个可用来代表它的新的 blog 类别;类似地,在构造 HTTP 请求 URI 时,用户照片共享的名称(由 getNameOfPhotoShare() 方法提供)可用作 blog 的名称。用户无需知道有关应用程序对 blog 的使用的任何内容 — 用户知道的是他们现在有办法可以组织自己的照片了。

其他单一请求动作的实现非常简单,只需复制并修改 createAlbum() 方法,以便它能有新的名称并使用不同的 HTTP 方法(例如,deleteAlbum()method: 'DELETE')。在所有情况下,都需要对表单按钮单击或页面重载进行响应以便利用 表 4 中的 URI 来 GET、POST、PUT 或 DELETE 内容。

在 JavaScript 中处理事务

熟悉了 dojo.io.bind() 函数后,单一请求动作并不难实现,但对多请求动作的处理则需要多加考虑。清单 4 显示了可用来创建新照片共享帐户的 HTML 表单和 JavaScript 函数。其中的 createPhotoShare() 方法发送两个 HTTP POST 请求:一个用来创建此用户文件共享上的目录,另一个用来创建组织照片所需的 blog。请注意针对后者的错误处理程序可撤销前者所做的工作,以使数据不会处在一种不一致的状态。


清单 4. 用 HTML 和 JavaScript 创建新的文件共享
                
    
<script type="text/javascript" src="/dojo.js">
</script>
    
<script>
dojo.require("dojo.io");
dojo.require("dojo.json");
dojo.require("dojo.widget.Form");
   
function createPhotoShare() 
{
    var userName = getUserName();
    
    //
    // create JSON objects to represent shared directory and blog
    //
    
    var directoryData = {
        path = userName + '-photos', 
        contenttype = 'directory', 
        owner = userName
    };
        
    var blogData = {
        DESCRIPTION : 'The photo blog for ' + userName,
        AUTHOR: userName, 
        HANDLE: userName + '-photos',
        TITLE: 'The photo blog for ' + userName
    };
        
    dojo.io.bind({
        url: '/resources/file_shares/' + userName,
        method: 'POST',
        sync: false,
        mimetype: 'text/json', 
        contentType: 'text/json',
        postContent:  dojo.json.serialize(directoryData),
        load: function(type, data) {
            alert("The new photo directory was created successfully.");
        },
        error: function(type, err) {
            alert(dojo.errorToString(err)); 
        }
    });
    
    dojo.io.bind({
        url: '/resources/blogs',
        method: 'POST',
        sync: false,
        mimetype: 'text/json', 
        contentType: 'text/json',
        postContent:  dojo.json.serialize(blogData),
        load: function(type, data) {
            alert("The new photo blog was created successfully.");
        },
        error: function(type, err) {
            alert("Could not complete creation of photo share.");
            deletePhotoShare();
        }
    });
}
    
function deletePhotoShare()
{
    var user = getUserName();
        
    dojo.io.bind({
        url: '/resources/file_shares/' + user + '/' + user + '-photos',
        method: 'POST',
        sync: false,
        headers: { 'X-Method-Override' : 'DELETE' }, 
        load: function(type, data) {
            alert("The photo directory was deleted successfully.");
        },
        error: function(type, err) {
            alert(dojo.errorToString(err)); 
        }
    });
    
    dojo.io.bind({
        url: '/resources/blogs/' + user + '-photos',
        method: 'POST',
        sync: false,
        headers: { 'X-Method-Override' : 'DELETE' }, 
        load: function(type, data) {
            alert("The photo blog was deleted successfully.");
        },
        error: function(type, err) {
            alert(dojo.errorToString(err));
        }
    });
}
    
function getUserName()
{
    return <%= _gc.get("/request/subject/remoteUser"); %>
}
    
</script>
    
...
    
<form dojoType="form" id="CreateAlbumForm" name="CreateAlbumForm">
  <input type="button" onClick="createPhotoShare();" value="Create My Photo Share!" />
</form>
  

清单 4 中需要理解的东西很多,所以请多加注意。HTML 表单只不过是一个按钮,用户可以在其上单击以创建自己名下的照片共享帐户。默认的行为是使用模式 user-photos 命名用户帐号(以及连带的文件共享和 blog),其中的 user 是用户的登录 ID。可以使用这个帐号名称来构造 createPhotoShare() 函数中的 HTTP POST 请求和 deletePhotoShare() 函数中的 HTTP DELETE 请求。在这两个函数中,均可以使用 dojo.io.bind() 来填入 HTTP 请求数据及处理响应。此代码最有趣的地方是前面提到的事务管理和用以表示新的共享目录和 blog 的 JSON 对象的创建,二者都在 createPhotoShares() 中进行。后者需要您知道,为了能被组件所接受,哪些字段是 JSON 对象所必需要具有的;这类信息可以在 projectzero.org 网站上的组件 API 文档中找到(请参阅 参考资料)。

表 4 中的另一个多请求动作所需要的请求与 清单 4 中的那些请求稍有不同,但总的结构应该大体相同:用能撤销所有已完成任务的错误处理程序来多次调用 dojo.io.bind()。这里惟一的区别是与照片或帐户删除所关联的那些动作;一旦删除了部分帐户数据,即使某些部分未成功删除,也必须要完成此任务。这可以通过执行服务器端脚本中(可以在其中执行真正的数据库事务)的所有删除任务加以避免,但对该方法的介绍已经超出了本文的范围。

请花些时间留意一下,现在,针对完全由 blog 和共享目录构成的后端所使用的还是以照片为中心的名称和值。当用户访问这个照片共享网站时,如果不查看 HTML 源代码,他们将不会看到任何对 blog 或文件共享的引用,这就让应用程序看起来很有整体性,尽管底层是分块的。最后一点值得一提的是,这两个组件在照片共享的范围之外也很有用,这就使应用程序比原来计划的还要功能强大。这些组件安装完毕之后,您自然可以使用它们来为用户托管 “常规” blog 和非照片文件共享,所有这些均可在一个网站上实现。漂亮!

结束语

至此,您已经看到了 Zero 组件如何能通过其 RESTful 接口相互组合在一起以提供丰富和功能强大的 Internet 应用程序。这种实现照片共享应用程序的方式构建在面向服务的架构和 RESTful 资源之上,其结果是该实现可以尽量多地重用代码并且大部分定制代码都在用户界面中。与 REST 本身一样,照片共享应用程序并非真正的代码集合,而更像是一种架构风格。用 Zero 构建其他类型应用程序的开发人员在计划自己的实现时,都应该仔细分析和考虑 Zero 所能提供的组件。


参考资料

学习

获得产品和技术

  • 下载 Project Zero 并立即开始应用在本文中所学到的技能。

讨论

关于作者

Dan Jemiolo developerWorks 投稿作者

Dan Jemiolo 是 IBM 位于美国北卡罗来纳州 Research Triangle Park 的 Project Zero 团队的一名咨询软件工程师。目前主要负责 Zero 平台的可重用组件及其服务目录的工作。他的工作包括 Apache Muse 2.0 的设计和开发,也曾参加过 OASIS Web 服务标准组织。三年前他从 Rensselaer Polytechnic Institute 获得了计算机科学的理学硕士学位,之后加入了 IBM。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


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


忘记密码?
更改您的密码

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

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

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

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

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


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

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=269849
ArticleTitle=利用 Project Zero 和 REST 设计原理创建相册应用程序
publish-date=11162007
author1-email=danjemiolo@us.ibm.com
author1-email-cc=ruterbo@us.ibm.com

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。