通过编程访问Google账户和服务

本文介绍通过桌面程序访问Google账户和Google服务的方法。以Google Calendar服务为例,使用Google Calendar API 2.0版。

1、Google账户登录
通过程序登录Google账户的方法有很多,最简单的方法是使用ClientLogin这个API,在桌面程序中一般可以使用这种方式。为了测试,可以直接在浏览器中调用这个API来看结果,URL类似这样:

https://www.google.com/accounts/ClientLogin?accountType=HOSTED_OR_GOOGLE&Email=YourAccount@gmail.com&Passwd=PASSWORD&service=xapi&source=YourCompany-YourApp-Version

其中Email=YourAccount@gmail.com是你的Google账户Email地址,Passwd=PASSWORD是账户的密码。service是要访问的服务名,对于Google Calendar服务,要传递cl。source是登录来源,也就是你的程序的名称之类。注意上面的关键字是区分大小写的,比如“Email=”中的“E”一定要大写。

以REALbasic语言为例,来看看怎么用程序来处理。在这里我们使用同步模式的HTTPSecureSocket来发送请求。
Dim https As New HTTPSecureSocket
https.Yield=True // 同意让渡CPU时间,以防在发送请求过程中界面长时间停止响应
Const PostURL="https://www.google.com/accounts/ClientLogin"
Dim form As New Dictionary // 使用字典组装表单,这比用“+”运算符连接字符串来组装URL要方便
form.Value("accountType")="HOSTED_OR_GOOGLE"
form.Value("Email")=“YourAccount@gmail.com"
form.Value("Passwd")="PASSWORD"
form.Value("service")="cl" // 指定访问Google Calendar服务
form.Value("source")="REALSoftware-REALStudio-"+RBVersionString
https.SetFormData(form) // 将表单指派给https,会对特殊字符编码,比如空格会被转为“%20”。
Dim ResultValue As String
ResultValue=https.Post(PostURL,20) // POST表单到指定地址,20秒无返回则自动超时

如果成功(https.HTTPStatusCode=200),ResultValue中将得到类似这样的3行返回结果(失败的话https.HTTPStatusCode=403并会返回错误信息):
SID=DQAAAH4AAADifYYKy7p9Lg7KdT4BW3zb7RvFmKJm7iJgQ2hjpTG6CLhVnQPFybD8t_odSpU5ZgODxcXnGi50cc-Ky8u-O0KaQiCEydE9RurCkoLEywY7a6trnN6mQW5SmcgjiNCwqiIzhAvQcu-0RFrvKQNJX78GQE4fFE_fs4uX2GTYleQGQg
LSID=DQAAAIAAAABWZhQlKvg2elXVWVttR23V2AWMu57XXvOiJJbGrHlUZW-96nsaokBwCzxs8_edWSDZOU3lU71R14u8QntrODC7WAFlBMkG04GZzIND7WPqX73t--dUAkYVlDoyfwP4N7bTNrg5VsOeeYervrxs_ADg8MBpqlnw-bbjIcyIub8org
Auth=DQAAAIAAAABWZhQlKvg2elXVWVttR23V2AWMu57XXvOiJJbGrHlUZW-96nsaokBwCzxs8_edWSC5iZ0YUERmdfIogOo05uk-YFGm0na7lYS_yT-_BNOLSBZeDcwtOOp9osxbv_7oUdGHbfhOoDza6G-ISUZM4dYgkDNmWyOqjTUwwbWAbbjU4w

这里我们只需要使用最后一行“Auth=”后面的内容(称为验证令牌)。将它解析出来保存到字符串变量AuthToken中(解析过程略),然后要设置一下后续的Google账户访问时需要发送的HTTP报头:
https.SetRequestHeader("Authorization","GoogleLogin auth="+AuthToken) // 将验证令牌放入Authorization请求头中
https.SetRequestHeader("GData-Version","2") // 如本文开头所说,我们要使用2.0版的API,所以设置GData-Version头

2、调用Google Calendar API
下面这个API用于获取已登录的用户的所有日历:
https://www.google.com/calendar/feeds/default/allcalendars/full
要使用这个API,只要继续用上面那个设置好验证令牌的HTTPSecureSocket实例向这个地址发送GET命令即可。
Const AllCalendarURL="https://www.google.com/calendar/feeds/default/allcalendars/full"
dim v As String=https.Get(AllCalendarURL,20) // 这次用GET(不要用POST),超时仍然选20秒

但得到的返回值v中的多半不是XML格式的日历列表,而是像这样一个HTML页面:
-
-
- Moved Temporarily
-
-
-

Moved Temporarily


The document has moved https://www.google.com/calendar/feeds/default/allcalendars/full?gsessionid=gC7NAlsQASjnE_wXPF1kxQ">here.
-
-

此时,如果检查https的HTTPStatusCode属性,会发现它的值是302,而不是200(OK,正常)。 302在HTTP协议里是Redirect(重定向)的意思,重定向到了上面那个HTML中显示的地址。我们不需要从HTML里解析它,一则这样太麻烦,二则Google可以自己决定对于302这个状态应该返回什么样的正文文本,如果Google做了些改动,我们解析方法可能就失效了。那怎么办呢?应该想到HTTP协议返回的内容不光只有页面正文,还有页面报头。我的经验是,进行通过HTTP访问某个网站开放的API一类的开发,经常检查一下返回的页面的报头是个好习惯。可以通过HTTPSecureSocket的PageHeaders方法,或在调试器中看_pageHeaders属性来查看报头,对于上面的GET操作,我们会看到这样一组报头:
Expires: Sat, 31 Jul 2010 12:43:48 GMT
Date: Sat, 31 Jul 2010 12:43:48 GMT
Set-Cookie: S=calendar=gC7NAlsQASjnE_wXPF1kxQ;Expires=Sun, 31-Jul-2011 12:43:48 GMT;Secure
Location: https://www.google.com/calendar/feeds/default/allcalendars/full?gsessionid=gC7NAlsQASjnE_wXPF1kxQ
Content-Type: text/html; charset=UTF-8
Cache-Control: private, max-age=0
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE

可以看到Location头的值正是跳转目标的URL,和HTML中显示的URL一致。
那么我们也进行“重定向”,向新地址发送GET命令:
Dim headers As InternetHeaders=https.PageHeaders // 读出所有InternetHeaders
Dim Result As String
If headers<>Nil Then
  Dim RedirectTo As String=headers.Value("Location") // 读取Location头,以得到重定向地址
  If RedirectTo<>"" Then Result=https.Get(RedirectTo,20) // 继续GET,向新地址请求数据
End If

但是结果很可惜,我们得到了又一个类似上面那样的HTML和一个新的302,如果继续下去你会得到永无止境的大体相同的HTML和302状态。为什么会这样呢?比较一下各次得到的正文内容或Location头,你会发现差异在URL里的gsessionid=xxxxxxxx一段。这段内容给出了会话ID(或者叫会话种子,Seed),这意味着我们必须使用这个会话才能继续执行后续的API。

要注意,HTTP协议是一种无连接协议,客户端发送请求时建立连接,服务器响应请求返回了数据后,连接立即断开。对于Google API,为了避免每执行一个API函数就进行一次登陆验证,服务器端会以会话ID为索引保存一个上下文。相应地,在客户端向服务器发送请求时也必须同时发送这个会话ID,以使服务器知道它所保存的哪个上下文和这个客户端对应。因此客户端也要保存这个会话ID和一些信息,以便在发送请求时使用,客户端所保存的东西被称为Cookie。

在REALbasic语言的HTTPSecureSocket类中怎么使用这些“曲奇饼干”呢?回过头来再看上面的那组报头,发现有一个名为Set-Cookie报头,可以看到其中也包含gsessionid的内容。这就是我们要保存并在后续访问中使用的Cookie了。所谓的“在后续访问中使用”,意思就是在后续的请求中作为报头发送给服务器。因此可以不必使用额外的变量,只要像上面保存验证令牌和GData版本号那样将Cookie缓存到https对象的RequestHeader里面就行了。现在修改上面的代码如下:
Dim headers As InternetHeaders=https.PageHeaders // 读出所有InternetHeaders
Dim Result As String
If headers<>Nil Then
  Dim Cookie As String=headers.Value("Set-Cookie") // 从Set-Cookie头读出Google要求我们使用的Cookie
  Dim RedirectTo As String=headers.Value("Location") // 从Location读出重定向地址
  If RedirectTo<>"" Then
    If Cookie<>"" Then https.SetRequestHeader("Cookie",Cookie) // 将Cookie写入请求报头中
    Result=https.Get(RedirectTo,20) // 向新地址请求数据
  End If
End If

这回Result中将收到含有日历列表的一个XML格式的字符串(实际上是Google Data格式的,也可以是RSS或Atom格式)。接下来只要用XMLDocument和相关的类进行解析,就能获取我们想要的数据了。


(转载请注明来源:http://www.3exware.com/home/default.asp?id=77

[本日志由 kmzs 于 2010-07-31 09:46 PM 编辑]
文章来自: 本站原创
引用通告: 查看所有引用 | 我要引用此文章
Tags: REALbasic Google
相关日志:
评论: 0 | 引用: -11 | 查看次数: 11203
发表评论
昵 称:
密 码: 游客发言不需要密码.
邮 箱: 支持Gravatar头像.
网 址: 输入网址便于回访.
内 容:
验证码:
选 项:
虽然发表评论不用注册,但是为了保护您的发言权,建议您注册帐号.
字数限制 500 字 | UBB代码 关闭 | [img]标签 关闭