3
回答
EGL 开发 Web 2.0 程序
利用AWS快速构建适用于生产的无服务器应用程序,免费试用12个月>>>   

应用场景介绍

新浪微博作为现在国内最流行的网站社交类应用,它提供了丰富的 API,能让第三方调用写出功能丰富的应用程序。新浪微博提供了 13 类,共计上百个应用程序接口,包括微博接口,评论接口,用户接口等。更多关于新浪微博接口介绍可以参见“参考资料:新浪微博 API”。新浪官方微博也提供了很多版本的 SDK 下载,比如 Java,PHP 等,程序员可以选择自己熟悉的语言 SDK 进行开发。

EGL 的 Web 2.0 开发支持包括了前台所见即所得的界面开发,也包括支持后台的服务调用以及业务逻辑的实现。前台的界面应用和后台的业务逻辑处理能共享同样的数据结构定义, 比如封装了用户信息的 EGL 记录部件(record part),既能用在浏览器端的界面程序,也能用在处理业务逻辑的后台服务器程序。因此使用 EGL 一门语言就能完成前端到后端的程序开发,不需要同时精通两门语言才能开发出 Web 2.0 应用。

限于文章篇幅,本文挑选了使用微博过程中经常用到的功能,并使用 EGL 来开发一个可以部署在 Tomcat 等中间件上的迷你版新浪微博网页客户端。用到的主要接口有 :

  1. 微博用户认证:在读取微博之前,必须通过新浪的用户认证。新浪微博采用 OAuth 2.0 认证的方式,我们将会展示如何使用 EGL 完成 OAuth 2.0 认证。
  2. 取得朋友(关注人)的微博:微博最重要的一个功能就是读取朋友发的消息,我们将会展示如何使用 EGL 调用微博读取接口,并使用所见即所得的可视化编辑器编辑界面。
  3. 发布个人微博:除了读取关注人的微博外,微博发布是另外一个重要的功能。我们将会带领读者学习如何使用 EGL 来调用微博发布接口,并将它包装成一个控件,在可视化编辑器中使用它。

在文章的最后,我们对该微博程序做一些增强,把认证信息保存到本地数据库中。我们将会看到如何使用 EGL 来方便地写出与数据库交互的应用程序。

EDT 安装

在开始开发应用之前,我们先需要安装 EDT。读者可以参见“参考资料:EDT V0.8 安装”的步骤来安装 EDT,这里就不做详细介绍了。

EDT Web2.0 开发介绍

EDT 提供了编辑 Web 2.0 应用的可视化编辑器,让 EGL 程序员所见即所得地开发 Web 2.0 应用。关于可视化编辑器的资料可以参见“参考资料:使用 Rich UI 可视化编辑器(Visual Editor)及相关工具介绍”。

申请新浪微博应用

我们在开始正式实现迷你微博之前,先需要做一些准备工作。包括在新浪微博申请账户、微博应用,以及获取应用的 App key 和 App Secret 等。

注册新浪微博帐号

访问新浪微博首页 http://www.weibo.com,在主页上点击“立即注册微博”按钮,输入相关信息,可以直接登录并立即开通微博账户。

申请新浪应用

访问新浪应用开发页面 http://open.weibo.com/development,点击“创建应用”按钮,如图所示。


图 1. 创建应用
创建应用

选择应用类型为“客户端应用”,如图所示。


图 2. 选择类型为客户端应用
选择类型为客户端应用

在应用的申请页面中,我们逐项输入所需要的信息:

  • 应用名称:EDTSample。
  • 应用地址:http://www.edtsample.com。
  • 应用介绍:一个利用 EGL/EDT 开发的 Web2.0 客户端程序,它和新浪微博进行连接。
  • 域名绑定:选择否。
  • 应用分类:选择“网页应用”。
  • 标签:可以灵活添加,在这里我们选择“好友互动”。
选择“我已阅读并接受《新浪微博开发者协议》”并点击“创建”按钮,即成功创建新浪应用 EDTSample。

获取应用的 App Key 和 App Secret

访问我的应用页面 http://open.weibo.com/apps,点击刚创建的应用 EDTSample,在左侧出现的导航条上点击应用信息->基本信息,在右侧出现的内容中可以获取该应用的 App Key 和 App Secret,这两个信息在稍后的程序会用到。如下图所示。


图 3. 获取 App key 和 App Secret
选择类型为客户端应用

开发应用

新浪微博客户端程序的架构如图所示。主要有客户端、服务提供者和资源三部分组成。其中 :

  • 客户端的项目有 org.eclipse.edt.sinaweibo 以及与之相对应的部署后的项目 org.eclipse.edt.sinaweibo.deploy,主要包括用户授权认证界面、主界面(获取关注人微博、发布微博),它利用 HTTP 协议调用新浪微博提供的第三方 RESTful Service 以及利用 EGL 开发的用户数据库访问的 RESTful Service。
  • 后台的服务(service)项目有 org.eclipse.edt.sinaweibo.db 以及与之相对应的部署后的项目 org.eclipse.edt.sinaweibo.db.deploy,主要负责提供与数据库交互的 RESTful Service。
  • 资源部分由新浪微博开放平台和关系数据库组成。其中新浪微博开放平台主要提供 RESTful 的 API 用于用户授权认证以及资源的访问。

图 4. 新浪微博客户端程序架构图
新浪微博客户端程序架构图

应用授权和用户认证

在正式开始使用 EGL 进行编程之前,还需要花点时间来了解新浪应用程序申请、授权,以及用户验证的过程。

新浪微博提供了基于 REST API 的访问方式,但在访问前,需要用户进行身份授权。新浪微博提供了国际通用的授权方式 OAuth,并建议使用最新版本 OAuth 2.0。读者可以访问 OAuth 2.0 的官方网站 http://oauth.net/2/,如图所示是 OAuth2.0 授权机制的协议流。


图 5. OAuth 2.0 授权机制的协议流程
OAuth 2.0 授权机制的协议流程

具体的流程如下:

  1. 首先需要引导用户到如下的授权地址: https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID& response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI。 在这里,YOUR_REGISTERED_REDIRECT_URI 是指用户所申请应用的 URI。在该页面中,需要用户输入新浪微博的用户名和密码进行登录。
  2. 登录成功后,页面会跳转至 YOUR_REGISTERED_REDIRECT_URI/?code=CODE。其中 CODE 就是我们所需要的授权码。
  3. 接下来,我们可以根据得到的授权码以及我们所申请应用的 AppKey 和 AppSecret 来组成新的 URL,通过访问该 URL 来获取访问令牌 Access Token: https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID& client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code& redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE。 其中,YOUR_CLIENT_ID 指应用的 App Key,YOUR_CLIENT_SECRET 指应用的 App Secret。在访问的同时,还需要将 App Key 和 App Secret 的 Base64 加密字符串以 Basic 的方式加入到 HTTP 请求的 Header 中,我们会在接下来介绍怎样在 EGL 中实现。
  4. 如果请求访问成功,授权服务器会以 JSON 的格式返回访问令牌(Access Token),如: {"access_token":"2.00a7Qi8C02rOA46adf4fa5789dg1QD","expires_in":86400,"remind_in":"79876","uid":"2241776320"}
到此为止,授权过程就结束了,我们通过得到的访问令牌去访问各种 RESTful API 来获取资源。 EDT 工作空间及项目设置
  1. 启动 EDT,选择一个新的工作空间。选择 File -> New -> EGL Project,弹出一个标题为 EGL Project 的对话框,在 Project name 中我们输入 org.eclipse.edt.sinaweibo,在 Template 中选择 Web 2.0 client application,点击 Finish 按钮。
  2. 切换到 Project Explorer 视图,可以看到有三个 EGL 项目,其中 org.eclipse.edt.rui.widgets_0.8.0 和 org.eclipse.edt.rui.dojo.remote_0.8.0 是 Web 2.0 开发所需要的依赖项目。
  3. 用鼠标右击 org.eclipse.edt.sinaweibo/EGLSource,在右键菜单中,选择 New -> Package,创建以下的包:
    • org.eclipse.edt.sinaweibo.common
    • org.eclipse.edt.sinaweibo.records
    • org.eclipse.edt.sinaweibo.ui
    • org.eclipse.edt.sinaweibo.widget
    其中,org.eclipse.edt.sinaweibo.common 包中存放的是项目所需要的公共类库(EGL Library),org.eclipse.edt.sinaweibo.records 存放的是所有底层的数据结构(EGL Record),org.eclipse.edt.sinaweibo.ui 存放的是所有的界面组件(Rich UI Handler),而 org.eclipse.edt.sinaweibo.widget 存放的则是自定义的控件(Rich UI Widget)。
开发公共类库

在设计界面和业务逻辑之前需要创建一个公共类库,用来存储项目所需要的 URL、App key/App Secret,Access Token 等信息。鼠标右击包 org.eclipse.edt.sinaweibo.common,右键菜单选择 New -> Library,命名为 Utility,并选择 Basic 模板,点击 Finish 完成 Library 的创建,并替换成清单 1 所示源代码。


清单 1. 公共类库清单

 library Utility 	
  // 新浪应用程序的 App Key 			
  const appKey string = "339912851"; 
  // 新浪应用程序的 App Secret 
  const appSecret string = "f5476516b85ab90a909f36f32032fcad"; 
  //App Key 和 App Secret 的 BASE64 加密字符串
  const baseStringOfKeySecret string = "MzM5OTEyODUxOmY1NDc2NTE2Yj 
		 g1YWI5MGE5MDlmMzZmMzIwMzJmY2Fk"; 
				
  uid string = "2241776320"; // 用户的 UID 
  authenticationCode string; // 用户授权码
  accessTokenRec AccessTokenRec; // 用户访问令牌
 
  // 新浪微博的转向地址
  const redirectUrl string = "www.edtsample.com"; 
  // 获取授权码的 URL 
  authenticationCodeUrl string = 
    "https://api.weibo.com/oauth2/authorize?client_id=" + 
    Utility.appKey + "&response_type=code&redirect_uri=" + 
    Utility.redirectUrl; 
  // 获取访问令牌的 URL 
  accessTokenUrl string = 
    "https://api.weibo.com/oauth2/access_token?client_id=" 
	 + Utility.appKey + "&client_secret=" + Utility.appSecret 
	 + "&grant_type=authorization_code&redirect_uri=" 
	 + Utility.redirectUrl + "&code="; 
  // 获取关注人微博的 URL 
  const getFriendsTimelineUrl string = 
    "https://api.weibo.com/2/statuses/friends_timeline.json"; 
  // 发布微博的 RUL 
  publishUrl string = 
    "https://api.t.sina.com.cn/statuses/update.json? 
	 source="+appKey+"&status="; 
 end 

利用向导生成 EGL 记录部件

然后我们需要利用 EGL 记录向导来生成相应的记录部件,该记录部件是用于存储访问令牌的底层数据结构。我们将上一步生成的 JSON 字符串保存为一个文本文件,如 access_token.json。用鼠标右击包 org.eclipse.edt.sinaweibo.records,在右键菜单中,选择 New -> Record,弹出 New EGL Record 对话框。在 Name 中,输入 AccessTokenRec,并选择 Records from JSON 模板,点击 Next 按钮。选择“Create from a file”,点击 Browser 按钮并选择刚刚保存的 JSON 文件 access_token.json,点击 Next 预览生成的 Record,最后点击 Finish 完成向导。

设计用户认证界面

接下来开始设计用户认证的界面。右击包 org.eclipse.edt.sinaweibo.ui,右键菜单中选择 New -> Handler,弹出 New EGL Handler 对话框。在 Name 中输入 LoginHandler,选择 Rich UI Handler 模板,点击 Finish 完成向导。这时 LoginHandler.egl 会默认以 Visual Editor 打开并处于 Design 的模式,我们可以开始进行页面的设计和编辑。

在设计页面之前,我们需要考虑一下页面布局。EDT 默认会在页面上生成一个 GridLayout,这个相当于 HTML 中的表格。我们需要往表格中拖拽一些 Box(相当于 HTML 中的 Div)并组成一个嵌套层次结构,如图所示。


图 6. 用户认证页面的布局及和界面的对应关系
用户认证页面的布局及和界面的对应关系

查看大图

我们在 GridLayout 的某一个单元格中放置一个 mainBox,而 mainBox 下又嵌套子元素 Box1、Box2 和 Box3,每个子元素下面会嵌套相应的控件。图 6 标明了每个 Box 和与之相对应的界面之间的映射关系。其中各个 Box 的内容涵义如下:

  • Box1 由图像控件 Image1 和 HTML 控件 HTML 组成。Image1 是一个图片,用于指示认证的第一步 STEP1;HTML 由带有图片“用微博帐号登录”的超链接组成,点击该超链接即可引导用户进入新浪微博用户授权认证页面。
  • Box2 由图像控件 Image2、Dojo 文本域 TextField 以及图像控件 Image3 组成。Image2 与 Image1 相似,用于指示认证的第二步 STEP2;TextField 用于输入授权码,我们会在下面的章节介绍如何获取授权码;Image3 是一个图片“Connect”,点击该图片,即可访问并获取 Access Token。
  • Box3 由图像控件 Image3 和 Dojo 文本域 TextField1 组成。Image3 同样是用来指示第三步 STEP3;TextField1 用于显示获取到的访问令牌。

在了解界面布局后,我们开始来设计页面。首先,从 Pallete 绘图板的 Layout 面板中,用鼠标拖拽一个 Box 到页面的 Layout 的第一个单元格中,并命名为 mainBox。用鼠标选择 mianBox,在 Properties 视图中设置 Column 属性为 1。用相同的方法拖拽另外三个 Box 到 mainBox 中,形成图 6 所示的布局结构。做完这几步后,我们可以得到图 7 所示的界面框架。


图 7. 页面布局
页面布局

查看大图

用鼠标选中 Box1,在 Display and Input 面板中拖拽 Image 和 HTML 到 Box1 中,并命名为 Image1 和 HTML,同时设置 Image1 的属性 src 值为 images/step1blue.png。

将样例程序中的 org.eclipse.edt.sinaweibo 项目展开,将 WebContent/images 中的图片拷贝到工作空间的相应的目录下。切换到 Source 模式下,编辑 HTML 控件的属性。具体的细节,可以参见参考资料中样例程序。

用相同的方法,按照图所示的布局添加 Box2 和 Box3 的控件和相应的属性,来完成用户认证界面的设计。

在设计完界面后,我们开始添加用户认证的逻辑。新浪微博推荐使用 OAuth2.0 技术来验证用户。这部分介绍如何如何使用 EGL 语言来实现新浪微博的 OAuth2.0 验证。

获取授权码(Authorization Code)

获取授权码是通过 HTML 的超链接实现的,其中 Utility.authenticationCodeUrl 可以从 Utility.egl 中获取(以下类同),源代码为清单 2 所示:


清单 2. 获取授权码的超链接及相关变量

//超链接:  
HTML HTML{text = "<a target=\"_blank\" href=\"" 
  + Utility.authenticationCodeUrl 
  + "\"><img src=\"images/loginusingweibo.png\" 
  style=\"background-color:black\"></a>", marginLeft = 10}; 

//变量定义:  
const redirectUrl string = "www.edtsample.com"; 
 authenticationCodeUrl string = 
  "https://api.weibo.com/oauth2/authorize?client_id=" 
  + Utility.appKey + "&amp;response_type=code&amp;redirect_uri=" 
  + Utility.redirectUrl; 

在完成上述 EGL 编码后,我们来看一下获取授权码的效果。选择 org.eclipse.edt.sinaweibo/EGLSource/org.eclipse.edt.sinaweibo.ui/LoginHandler.egl, 在右键菜单中选择 Run As -> EGL Rich UI Application,则会启动一个外部浏览器显示用户认证页面。

点击页面中的“用微博帐号登录”图片,浏览器则转向一个新的窗口,引导用户进入新浪微博页面进行授权,如图所示:


图 8. 用户认证页面
用户认证页面

图 9. 新浪微博网站接入页面
新浪微博网站接入页面

在网站接入页面中,用户输入用户名和密码后点击“连接”按钮,如果授权成功则会跳向 redirectUrl 所指向的地址。如果该地址无法访问,则页面会显示“Sorry, that page doesnot exist!”,不管是否跳转成功,在地址栏都可以得到包含授权码的 URL,如:https://api.weibo.com/oauth2 /www.wed.com?code=dfdc7dd440bdef3479503dcec4fede60。字符串 dfdc7dd440bdef3479503dcec4fede60 即为我们所得到的授权码(Athentication Code),我们记录并拷贝该授权码用于下一阶段获取访问令牌。

获取访问令牌(Access Token)

获取访问令牌的业务逻辑我们写在 Connect 按钮的事件中,其中 authenticationCode 来自于 TextField 控件的文本。EGL 源码如清单 3 所示。


清单 3. 获取访问令牌的事件逻辑
 function Image3_onClick(event Event in) 
  Utility.authenticationCode = TextField.text; 
  Utility.accessTokenUrl = Utility.accessTokenUrl + Utility.authenticationCode; 
  http HttpRest{@Resource{}}; 
  http.request.headers = new Dictionary{Authorization = "Basic " 
    + Utility.baseStringOfKeySecret}; 
  call IRest.invokePost(Utility.accessTokenUrl, "") using http returning to 
    serviceCallback onException myException; 
 end 
来看一下效果。将授权码粘贴到步骤 2 中的文本框中,点击“Connect”按钮,程序从文本框中读取授权码,并开始请求访问令牌,请求成功后,会将访问令牌显示在步骤 3 的文本框中,如图所示。我们记录下该访问令牌,用于下一阶段(获取关注人微博、发布微博)。
图 10. 获取访问令牌
获取访问令牌

获取朋友(关注人)的微博

这部分主要是介绍如何使用 EDT 的只读的控件来显示关注人的微博。另外,这部分也会介绍如何使用 EDT 提供的自定义控件的功能来实现可重用的 Web2.0 控件的开发。

获取关注人微博 API 介绍

新浪微博提供了 RESTful API 来获取当前登录用户和所关注用户的最新微博,其 URL 为 https://api.weibo.com/2/statuses/friends_timeline.json,HTTP 请求方式为 GET 方法,支持的数据格式为 JSON。返回的数据格式如清单 4 所示。


清单 4 调用获取关注人微博 API 返回的数据格式
  { 
	"statuses": [ 
	 { 
	"created_at": "Tue May 31 17:46:55 +0800 2011", 
	"id": 11488058246, 
	"text": "求关注。",
	"source": "<a href="http://weibo.com" rel="nofollow"> 新浪微博 </a>", 
	"favorited": false, 
	"truncated": false, 
	"in_reply_to_status_id": "", 
	"in_reply_to_user_id": "", 
	"in_reply_to_screen_name": "", 
	"geo": null, 
	"mid": "5612814510546515491", 
	"reposts_count": 8, 
	"comments_count": 9, 
	"annotations": [], 
	"user": { 
	"id": 1404376560, 
	"screen_name": "zaku", 
	"name": "zaku", 
	"province": "11", 
	"city": "5", 
	"location": "北京 朝阳区", 
	"description": "人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。", 
	"url": "http://blog.sina.com.cn/zaku", 
	"profile_image_url": "http://tp1.sinaimg.cn/1404376560/50/0/1", 
	"domain": "zaku", 
	"gender": "m", 
	"followers_count": 1204, 
	"friends_count": 447, 
	"statuses_count": 2908, 
	"favourites_count": 0, 
	"created_at": "Fri Aug 28 00:00:00 +0800 2009", 
	"following": false, 
	"allow_all_act_msg": false, 
	"remark": "", 
	"geo_enabled": true, 
	"verified": false, 
	"allow_all_comment": true, 
	"avatar_large": "http://tp1.sinaimg.cn/1404376560/180/0/1", 
	"verified_reason": "", 
	"follow_me": false, 
	"online_status": 0, 
	"bi_followers_count": 215 
	 } 
	 }, 
	 ... 
	 ], 
	"previous_cursor": 0, 
	"next_cursor": 11488013766, 
	"total_number": 81655 
	 } 
  }
关于具体的 API 介绍,读者可以访问 http://open.weibo.com/wiki/2/statuses/friends_timeline 来了解相应的参数、错误码、访问限制等信息。 利用向导生成 EGL Record

我们按照 1.3.1 介绍的方法,将上一步返回的 JSON 格式的数据(清单 4)转换为 EGL Record,在包 org.eclipse.edt.sinaweibo.records 下生成相应的 EGL 记录 FriendsTimelineRec。这个记录部件是用于存储一条微博的基本数据,如创建时间、微博 ID、微博内容等。

自定义控件展示微博条目

在 Project Explorer 视图中,鼠标右击包 org.eclipse.edt.sinaweibo.widget,选择 New -> Handler,命名为 TimelinesWidget,并选择 Rich UI Widget 模板,点击 Finish 按钮完成向导。在自定义的控件 TimelinesWidget 中,我们提供了动态生成微博条目的函数 draw()。在主界面中,当获取到一条新的微博数据后,立即调用 draw() 函数来绘制相应的微博条目的界面,并将该控件附加到主界面上。draw() 函数如清单所示。


清单 5 自定义控件 TimelinesWidget 的 draw() 函数

 function draw() 
  theStatues Statuses1[] = friendsTimelineRec.statuses; 
  // 遍历 friendsTimelineRec 中所有的微博   
  for(index int from 1 to theStatues.getSize() by 1) 
     statue Statuses1 = theStatues[index]; 

     profile_image Image{layoutData = new GridLayoutData{ 
     row = index, column = 1, verticalAlignment = GridLayoutLib.VALIGN_TOP 
     }, src = statue.user.profile_image_url, paddingRight = 15}; 
     profile_link HyperLink{text = statue.user.name, href 
     = statue.user.profile_url}; 
     textBody HTML{text = statue.text}; 
     innerBox Box{columns = 1}; 
     innerBox.appendChildren([profile_link, textBody]); 

     item Box{children =[profile_image, innerBox], borderWidth = 1, 
     borderBottomStyle = "dotted", padding = 10, color = "Gray", 
     onMouseOver ::= backgroundcolorChange, onMouseOut ::= backgroundcolorFade}; 

     if(statue.bmiddle_pic != null) 
         thumbnail_pic EhnImageWidget{src = statue.thumbnail_pic, 
         normalSrc = statue.thumbnail_pic, bigSrc = statue.bmiddle_pic, 
         onClick ::= thumbnail_pic_onClick}; 
         innerBox.appendChildren([thumbnail_pic]); 
     end 

     tailBox Box{padding = 8, width = "100%"}; 
     created_at HTML{text = statue.created_at}; 
     source HTML{text = "&nbsp;&nbsp;&nbsp; 来自" 
     + statue.source}; 
     reposts_count HTML{text = "&nbsp;&nbsp;&nbsp;&nbsp; 转发" 
     + statue.reposts_count}; 
     comments_count HTML{text = "&nbsp;&nbsp; 评论" + statue.comments_count, 
     onClick ::= commentCountClick}; 
     tailBox.appendChildren([created_at, source, reposts_count, comments_count]); 
     innerBox.appendChildren([tailBox]); 
     TimelinesBox.appendChildren([item]); 
  end 
 end

创建微博客户端主页面

接下来,我们需要创建微博客户端的主页面。在包 org.eclipse.edt.sinaweibo.ui 中创建一个类型为 Rich UI Handler 的源文件 MainHandler.egl。按图 11 所示的页面布局来创建页面的框架。


图 11. 微博客户端主页面布局框架
微博客户端主页面布局框架

其中,mainBox 嵌套了子 Box:leftBox。leftBox 主要包括微博发布的控件(topBox)和微博内容控件(contentBox)。

我们再来给主页面 MainHandler 做一点页面上的美化,在这里,我们学习一下如何使用结合 Properties 视图和源代码两种方式来设置界面的属性。

  • 用鼠标选择 mainBox,切换到 Properties 视图下,在 General 标签页,设置 alignment 属性为 CENTER;在 Position 标签页设置 height 为 1000。如图所示。

    图 12. 在 Properties 视图中设置 Box 的属性
    微博客户端主页面布局框架

    查看大图

  • 选择 leftBox, 用同样的方法设置 columns 为 1,backgroundColor 为 White。
  • 选择 contentBox, 设置 columns 为 1,width 为 520。
  • 切换 MainHandler.egl 到 Source 模式,在 mainBox 的 EGL 源码中追加属性:style = "background-image:url(images/background.jpg);background-repeat: repeat-y;"。

到此为止,Box 的属性就设置完了,完成后的 EGL 源码如清单 6 所示。


清单 6 主页面布局的调整和美化
 mainBox Box{layoutData = new GridLayoutData{row = 1, column = 1}, 
 padding = 8, children =[leftBox], alignment = BoxLib.ALIGN_CENTER, 
 style = "background-image:url(images/background.jpg);background-repeat: 
 repeat-y;", height = "1000"}; 
 leftBox Box{padding = 8, columns = 1, backgroundColor = "White", 
 children =[topBox, contentBox]}; 
 topBox Box{}; 
 contentBox Box{padding = 8, columns = 1, width = "520"}; 
接下来,将获取微博的控件插入主页面。在 start() 函数中,加入函数 getFriendsTimeline() 以及相应的函数实现,具体 EGL 源代码如清单 7 所示。
清单 7 函数 getFriendsTimeline() 及其他函数的 EGL 源码
 function getFriendsTimeline() 
   http HttpRest{@Resource{}}; 
   http.request.headers = new Dictionary{Authorization = 
   "OAuth2 " + Utility.accessTokenRec.access_token}; 
   call IRest.invokeGet(Utility.getFriendsTimelineUrl) 
   using http returning to getFriendsTimelineCallback 
   onException getFriendsTimelineCallbackExp; 
 end 

 function getFriendsTimelineCallback(retResult string in) 
   friendsTimelineRec FriendsTimelineRec; 
   JsonLib.convertFromJSON(retResult, friendsTimelineRec); 
   
   timelinesWidget TimelinesWidget = new TimelinesWidget; 
   timelinesWidget.friendsTimelineRec = friendsTimelineRec; 
   timelinesWidget.draw(); 
   contentBox.removeChildren(); 
   contentBox.appendChildren([timelinesWidget.TimelinesBox]); 
 end 

 function getFriendsTimelineCallbackExp(exp AnyException in) 
   SysLib.writeStdout(exp.message); 
 end
到目前为止,主页面就创建完了,它包括获取微博的控件。我们可以切换 MainHandler 到 Preview 模式来预览效果,如图所示。
图 13. 微博主页面的预览效果
微博主页面的预览效果

查看大图

微博发布

微博发布 API 介绍

新浪微博也提供了发布微博的 API,其访问 URL 为 https://api.weibo.com/2/statuses/update.json,支持的 HTTP 方式为 POST 方法。返回的 JSON 格式和清单 4 很相似,具体的参数、访问权限等信息读者可以访问 http://open.weibo.com/wiki/2/statuses/update。

自定义控件发布微博

为了实现微薄发布的功能,我们实现一个自定义的控件:InputStatusWidget。该组件的页面设计主要包括输入控件 DojoTextArea、事件管理、第三方 REST 服务调用以及消息总线(InfoBus)。

在包 org.eclipse.edt.sinaweibo.widget 下新建一个 InputStatusWidget。在该自定义控件中,我们可以用拖拽的方式来设计页面,其做法和 1.3.1 中介绍的一样。微博发布的界面布局如图所示。


图 14. 微博发布自定义控件的页面布局
微博发布自定义控件的页面布局

查看大图

限于文章篇幅,这里就不再赘述页面的设计细节,读者可以自行去实现图示的布局和界面,并且可以参考样例的 EGL 源代码。其中,输入控件的实现代码和发布微博的事件逻辑如清单 8 所示。


清单 8 输入控件的实现代码和发布微博的事件逻辑
//输入控件的实现代码:  

contentTextArea DojoTextArea{width = "500", height = "80", 
  marginTop = -10, onKeyPress ::= contentTextArea_onKeyPress}; 

 function contentTextArea_onKeyPress(event Event in) 
     content = contentTextArea.text; 
     contentClip string = content.clip(); 
     if(contentClip != "") 
         publishImage.src = "images/publishonline.PNG"; 
         changeBox.children =[caculatorHTML]; 
         if(contentClip.length() < 280) 
             textCount string = 140 - mathlib.floor(contentClip.length() 
             / 2) as string; 
             caculatorHTML.text = "请文明发言,还可以输入" 
             + "<b>" + textCount 
             + "</b> 字"; 
         end 
     end 
 end 

//发布微博的事件逻辑:  

function publish(e Event in) 
   publishImage.setSrc("images/loading3.gif"); 

   status string = contentTextArea.text; 
   Utility.publishUrl = Utility.publishUrl + status; 

   http HttpRest?{@Resource{}}; 
   http.request.headers = new Dictionary{Authorization = "OAuth2 " 
   + Utility.accessTokenRec.access_token}; 
   call IRest.invokePost(Utility.publishUrl, status) using http returning 
   to publishStatusCallback onException publishStatusException; 
 end 

 function publishStatusCallback(retResult string in) 
   InfoBus.publish("status", "publish status ok"); 
   contentTextArea.text = ""; 
   publishImage.setSrc("images/publishoffline.PNG"); 
 end 

 function publishStatusException(exp AnyException in) 
   contentTextArea.text = ""; 
   publishImage.setSrc("images/publishoffline.PNG"); 
   SysLib.writeStdout(exp.message); 
 end 
在这里 EGL 调用第三方的 RESTful Service 用于实现微博的发布,在发布成功后的回调函数里,消息总线(InfoBus)会向主题为 status 的订阅器发布一条“publish status ok”的消息,同时清空输入框内容,并设置回发布按钮的图片。 修改主页面

EDT 的可视化编辑器(Visual Editor)具有非常好的扩展性,可以在绘图板上显示自定义的控件,用户可以直接拖拽这些控件到页面上。用户在定义控件时还可以指定 category 属性来设置控件在绘图板中的分类和显示位置。如图所示是我们拖拽微博发布控件 inputStatusWidget 到主页面 MainHandler 的示意图。


图 15. 拖拽自定义控件 inputStatusWidget 到主页面上
拖拽自定义控件 inputStatusWidget 到主页面上

查看大图

切换 MainHandler.egl 到 Source 模式下,我们可以看到在 Box 变量的声明后面,已经自动添加了由拖拽产生的语句 inputStatusWidget InputStatusWidget{}。在 start() 函数里,添加 initUI() 和 initInfobus() 函数。具体的代码如清单 9 所示。


清单 9 函数 initUI() 和 initInfobus() 的实现
 function initUI() 
   topBox.appendChild(inputStatusWidget.mainBox); 
 end 

 function initInfobus() 
   Infobus.subscribe("status", statusCallback); 
 end 

 function statusCallback(name string in, data any in) 
   if(data as string == "publish status ok") 
       job Job{runfunction = getFriendsTimeline}; 
       job.schedule(5000); 
   end 
 end 
这样发布微博控件及相关的业务逻辑就被加入到主页面 MainHandler 中了,我们可以预览一下界面,如图所示,这是我们最终得到的效果。
图 16. 微博客户端的预览效果
拖拽自定义控件 inputStatusWidget 到主页面上

查看大图

在输入框中输入一段字符串,点击发布按钮开始发布一条微博。微博发布成功后,扫等片刻,就可以看到页面有局部的刷新,刚刚发布的微博就会显示在第一条。

将 Access Token 保存到数据库

最后,我们对这个应用程序做一点增强,把用户授权后得到的 Access Token 存入数据库中,如果用户在 24 小时内访问的话只需从数据库里读出 Access Token,而无需要重新开始认证过程。这部分会介绍如何在 EDT 里快速的开发一个数据库相关的应用。 在开发数据库项目之前,需要自行安装 IBM DB2 或利用已有的 DB2 来创建名为 WEIBO 的数据库,并创建一个名为 EGL 的模式以及表 TOKEN:


清单 10 创建表 TOKEN
 CREATE TABLE EGL.Token ( 
   UID varchar(255) NOT NULL PRIMARY KEY, 
   ACCESSTOKEN varchar(255) 
 ); 
关于如何安装 DB2 及创建数据库,读者可以访问 DB2 的官方网站 http://www-01.ibm.com/software/data/db2/ 来获取最新资料。需要注意的是,在这里,读者也可以使用其他的数据库,如 Apache Derby, Oracle,MySQL 等。 开发数据库访问服务

在创建完数据库和表 Token 之后。我们需要新建一个 EGL REST Service 用来提供数据库的访问(一般都是对表的增、删、查、改),在这里利用 Data Access Application 向导提供的功能来自动生成数据库访问所需要的 Record 以及 Service。

  1. 切换到 Database Development 透视图下,创建一个名为 MyDB2 的数据库连接,该连接指向刚建立的数据库 WEIBO。读者可以查看 Eclipse 的相关资料来了解如何创建数据库连接。
  2. 菜单选择 File – > New -> EGL Project, 新建一个 EGL 项目 org.eclipse.edt.sinaweibo.db,注意选择 Web2.0 client application with services 作为模板,这表示该项目是一个 Service 项目。
  3. 在新创建的项目中新建一个 EGL Service,输入 org.eclipse.edt.sinaweibo.db 作为包名,并选择“Service from a database“作为模板 , 点击 Next。如图所示。

    图 17. New EGL Service 配置
    New EGL Service 配置

  4. 在 Database Connection 中,选择前面创建的数据库连接 MyDB2,在 Tables 中选择 EGL.TOKEN,并勾选上“Qualify table names with schema”和“Create SQL database binding in the development deployment descriptor”。点击 Finish 按钮完成 Service 的创建,如图所示。用户也可以点击 Next 按钮选择预览生成的 EGL 源代码。

    图 18. 选择数据库的相关配置
    选择数据库的相关配置

  5. 最后,需要添加一下服务的部署信息。使用 EGL Deployment Descriptor Editor 打开 org_eclipse_edt_sinaweibo_db.egldd 文件,切换到 Service Deployment 下,点击 Add …按钮,在 弹出的 Add Web Service 对话框中,选择 org.eclipse.edt.sinaweibo.db.WEIBO.DataAccess(org.eclipse.edt.sinaweibo.db), 点击 Add-> 按钮,并点击 Finish 按钮,如图所示。

    图 19. 增加 Web Service 到部署描述符中
    增加 Web Service 到部署描述符中

    查看大图

到目前就位置,访问数据库的 REST Service 就创建好了。我们切换到 Project Explorer 视图下看一下生成的目录结构。如图所示。


图 20. 数据库访问服务项目的目录结构
数据库访问服务项目的目录结构

查看大图

其中,Token.egl 是由表 EGL.TOKEN 生成的 EGL 记录。而 DataAccess.egl 是由 EDT 生成的和数据库访问相关的服务,包括增加一条或多条 Token,删除 Token 等函数。这些函数我们需要在下一阶段来调用。

调用 EGL REST Service

接下来,我们需要在客户端项目 org.eclipse.edt.sinaweibo 中来调用上一步创建的 REST Service,步骤如下:

  1. 配置客户端服务绑定。EDT 提供了两种服务绑定的方式:基于工作空间(workspace URI)和基于部署的服务(deployed URI)。workspace URI 是指客户端引用工作空间中服务提供者的源代码,这种方式适合开发模式,并且可以支持 EGL 的调试(Debug);而 deployed URI 则是指客户端应用已部署的服务,通常用于最终部署的应用程序中。在这一章节,我们使用第一种方式。

    使用 EGL Deployment Descriptor Editor 打开 org_eclipse_edt_sinaweibo.egldd 文件,切换到 Resource Binding 标签页,点击 Add …按钮,添加一个 Rest Binding,指定 Binding 的名称为 dataaccess,Base URI 为 workspace://org.eclipse.edt.sinaweibo.db /org.eclipse.edt.sinaweibo.db.WEIBO.DataAccess。如图所示。



    图 21. 配置客户端服务绑定
    配置客户端服务绑定

    查看大图

  2. 存储访问令牌。在用户认证页面 LoginHandler.egl 中,在获取用户的 Access Token 以及 UID 以后,我们利用上一步创建的 RESTful Service 来存储该 UID 及 Access Token。在 LoginHandler 的 serviceCallback() 函数中,添加调用 Service 的 EGL 代码,如清单 11 所示:

    清单 11 存储访问令牌到数据库
     // Save Access Token into Database 
     token Token{uid = Utility.uid, accesstoken = Utility.accessTokenRec.access_token}; 
     dataaccess IDataAccess?{@Resource{}}; 
     call dataaccess.addToken(token) returning to addTokenCallback; 
获取用户的 Access Token

在主页面 MainHandler.egl 中,由于用户的 UID 是唯一的,我们可以调用 RESTful Service,通过传递用户 UID 参数来获取用户的 Access Token。同时,我们需要修改主页面 MainHandler,在 start() 函数中,添加 retrieveToken() 函数,与之相对应的函数实现如清单 12 所示。


清单 12 存获取访问令牌的函数
 function retrieveToken() 
  dataaccess IDataAccess?{@Resource{}}; 
  call dataaccess.getTokenById(Utility.uid) returning to getTokenByIdCallback; 
 end 

 function getTokenByIdCallback(retResult Token? in) 
  Utility.accessTokenRec.uid = retResult.uid; 
  Utility.accessTokenRec.access_token = retResult.accesstoken; 
  getFriendsTimeline();  //Get Friends Info 
 end 
注意我们将原 start() 函数中的函数 getFriendsTimeline() 移到回调函数 getTokenByIdCallback() 的结尾,这样做的目的是为了保持同步。只有成功取得数据库中的访问令牌,才能通过令牌去获取最新微博和登录用户的个人信息。具体的 EGL 源文件读者可以参考样例程序。 调试客户端程序

EDT 提供了调试(Debug)的功能。我们可以在 MainHandler.egl 的函数 retrieveToken() 和 getTokenByIdCallback() 中设置断点,右击 MainHandler.egl,选择 Debug As -> EGL Rich UI Application,则会启动一个外部浏览器,同时 EDT 提示用户切换到 Debug 透视图下进行调试,如图所示。


图 22. 调试客户端程序
调试客户端程序

部署程序

最后,我们需要将开发完毕并通过测试的应用程序部署到 Tomcat 应用服务器上。
  1. 在部署之前,我们需要将 REST Service Binding 的 Base URI 的格式由 workspace 方式调整为 deployed 的方式。
    • 选择 org.eclipse.edt.sinaweibo/EGLSource/org_eclipse_edt_sinaweibo.egldd,右键菜单中选择“Open With- 》 EGL Deployment Descriptor Editor”。
    • 切换到 Resource Binding 标签页下,在 Resource Binding 中选中 dataaccess,将右侧的 Base URI 由 workspace://org.eclipse.edt.sinaweibo.db/org.eclipse.edt.sinaweibo.db.WEIBO.DataAccess 改为 http://<YOUR_IP_ADDRESS>:<PORT>/org.eclipse.edt.sinaweibo.db.deploy/restservices/DataAccess 其中 YOUR_IP_ADDRESS 为应用服务器的 IP 地址,PORT 为应用服务器的端口。如我们利用本地的 Tomcat 服务器,则 Base URI 为:http://localhost:8080/org.eclipse.edt.sinaweibo.db.deploy/restservices /DataAccess。
  2. 切换到 Overview 标签页,在 Deployment Target 中,点击 New …按钮,新建一个目标运行时为 Tomcat v6.0 的动态 Web 项目(Dynamic Web Project):org.eclipse.edt.sinaweibo.deploy。
  3. 保存 org_eclipse_edt_sinaweibo.egldd 文件,在右上角有个带有提示“Deploy this EGL Descriptor”的按钮,点击该按钮进行部署。EDT 会将前面开发的程序中所生成的 Java、JavaScript、HTML、CSS 以及相应的配置文件拷贝到目标项目中。
  4. 在部署完毕后,我们启动应用服务器,在浏览器中运行 http://localhost:8080/org.eclipse.edt.sinaweibo.deploy/MainHandler.html,可以看到页面和在前面章节中测试的效果一致。

总结

我们在这篇文章里介绍了如何使用 EDT 开发一个迷你新浪微博客户端程序,了解了集成开发环境提供的所见即所得的 Web 2.0 应用开发方式,还知道了如何使用 EDT 进行快速的数据库相关的应用开发。系列文章的下面一篇我们将学习如何使用 EDT 进行移动平台的开发。

原文出处:IBM developerWorks

举报
IBMdW
发帖于6年前 3回/2K+阅
顶部