Google Data on Rails

Eric Bidelman, Nhóm API dữ liệu của Google
Tháng 2 năm 2009

Giới thiệu

"Ruby nằm ở đâu trong danh sách thư viện ứng dụng?"

Với mong muốn đáp ứng nhu cầu của các nhà phát triển và sự phổ biến lâu dài của Ruby on Rails (RoR), đồng nghiệp Jeff Fisher của tôi đã tạo ra một thư viện tiện ích Ruby từ sâu trong lòng Núi Doom. Xin lưu ý rằng đây không phải là một thư viện ứng dụng đầy đủ, nhưng nó xử lý các nguyên tắc cơ bản như xác thực và thao tác XML cơ bản. Bạn cũng cần làm việc trực tiếp với nguồn cấp dữ liệu Atom bằng cách sử dụng mô-đun REXML và XPath.

Đối tượng

Bài viết này dành cho những nhà phát triển muốn truy cập vào Google Data API bằng Ruby, cụ thể là Ruby on Rails. Hướng dẫn này giả định rằng người đọc đã quen thuộc với ngôn ngữ lập trình Ruby và khung phát triển web Rails. Tôi tập trung vào Documents List API cho hầu hết các mẫu, nhưng bạn có thể áp dụng các khái niệm tương tự cho bất kỳ Data API nào.

Bắt đầu

Yêu cầu

Cài đặt Thư viện tiện ích dữ liệu của Google cho Ruby

Để lấy thư viện này, bạn có thể tải trực tiếp nguồn thư viện xuống từ dịch vụ lưu trữ dự án hoặc cài đặt gem:

sudo gem install gdata

Lưu ý: Để chắc chắn, hãy chạy gem list --local để xác minh rằng gem đã được cài đặt đúng cách.

Xác thực

ClientLogin

ClientLogin cho phép ứng dụng của bạn đăng nhập người dùng vào tài khoản Google hoặc G Suite của họ theo cách lập trình. Sau khi xác thực thông tin đăng nhập của người dùng, Google sẽ phát hành một mã thông báo Uỷ quyền để tham chiếu trong các yêu cầu API tiếp theo. Mã thông báo vẫn hợp lệ trong một khoảng thời gian nhất định, do dịch vụ Google mà bạn đang làm việc xác định. Vì lý do bảo mật và để mang lại trải nghiệm tốt nhất cho người dùng, bạn chỉ nên sử dụng ClientLogin khi phát triển các ứng dụng đã cài đặt trên máy tính. Đối với các ứng dụng web, bạn nên sử dụng AuthSub hoặc OAuth.

Thư viện Ruby có một lớp ứng dụng cho từng API. Ví dụ: sử dụng đoạn mã sau để đăng nhập user@gmail.com vào Documents List Data API:

client = GData::Client::DocList.new
client.clientlogin('user@gmail.com', 'pa$$word')

The YouTube Data API would be:

client = GData::Client::YouTube.new
client.clientlogin('user@gmail.com', 'pa$$word')

Xem danh sách đầy đủ các lớp dịch vụ đã triển khai. Nếu dịch vụ không có lớp ứng dụng, hãy sử dụng lớp GData::Client::Base. Ví dụ: mã sau đây buộc người dùng đăng nhập bằng tài khoản G Suite.

client_login_handler = GData::Auth::ClientLogin.new('writely', :account_type => 'HOSTED')
token = client_login_handler.get_token('user@example.com', 'pa$$word', 'google-RailsArticleSample-v1')
client = GData::Client::Base.new(:auth_handler => client_login_handler)

Lưu ý: Theo mặc định, thư viện này sử dụng HOSTED_OR_GOOGLE cho accountType. Các giá trị có thể là HOSTED_OR_GOOGLE, HOSTED hoặc GOOGLE.

Một trong những nhược điểm của việc sử dụng ClientLogin là ứng dụng của bạn có thể nhận được các thử thách CAPTCHA khi đăng nhập không thành công. Nếu điều đó xảy ra, bạn có thể xử lý lỗi bằng cách gọi phương thức clientlogin() kèm theo các tham số bổ sung: client.clientlogin(username, password, captcha_token, captcha_answer). Hãy tham khảo tài liệu đầy đủ về Xác thực cho các ứng dụng đã cài đặt để biết thêm thông tin về cách xử lý CAPTCHA.

AuthSub

Tạo URL AuthSubRequest

scope = 'http://www.google.com/calendar/feeds/'
next_url = 'http://example.com/change/to/your/app'
secure = false  # set secure = true for signed AuthSub requests
sess = true
authsub_link = GData::Auth::AuthSub.get_url(next_url, scope, secure, sess)

Khối mã trước đó sẽ tạo URL sau đây trong authsub_link:

https://www.google.com/accounts/AuthSubRequest?next=http%3A%2F%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=http%3A%2F%2Fwww.google.com%2Fcalendar%2Ffeeds%2F&session=1&secure=0

Bạn cũng có thể sử dụng phương thức authsub_url của đối tượng máy khách. Mỗi lớp dịch vụ đều đã đặt một thuộc tính authsub_scope mặc định nên bạn không cần chỉ định thuộc tính của riêng mình.

client = GData::Client::DocList.new
next_url = 'http://example.com/change/to/your/app'
secure = false  # set secure = true for signed AuthSub requests
sess = true
domain = 'example.com'  # force users to login to a G Suite hosted domain
authsub_link = client.authsub_url(next_url, secure, sess, domain)

Khối mã trước đó sẽ tạo URL sau:

https://www.google.com/accounts/AuthSubRequest?next=http%3A%2F%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=http%3A%2F%2Fdocs.google.com%2Ffeeds%2F&session=1&secure=0&hd=example.com

Nâng cấp mã thông báo dùng một lần thành mã thông báo phiên

AuthSub sẽ chuyển hướng người dùng trở lại http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN sau khi họ cấp quyền truy cập vào dữ liệu của mình. Xin lưu ý rằng URL này chỉ là next_url của chúng tôi với mã thông báo dùng một lần được thêm vào dưới dạng tham số truy vấn.

Tiếp theo, hãy trao đổi mã thông báo dùng một lần để lấy mã thông báo phiên có thời hạn sử dụng lâu dài:

client.authsub_token = params[:token] # extract the single-use token from the URL query params
session[:token] = client.auth_handler.upgrade()
client.authsub_token = session[:token] if session[:token]

Secure AuthSub cũng tương tự như vậy. Bạn chỉ cần thêm khoá riêng tư trước khi nâng cấp mã thông báo:

PRIVATE_KEY = '/path/to/private_key.pem'

client.authsub_token = params[:token]
client.authsub_private_key = PRIVATE_KEY
session[:token] = client.auth_handler.upgrade()
client.authsub_token = session[:token] if session[:token]

Lưu ý: Để sử dụng mã thông báo bảo mật, hãy nhớ đặt secure=true khi yêu cầu mã thông báo dùng một lần. Xem phần Tạo URL AuthSubRequest ở trên.

Quản lý mã thông báo

AuthSub cung cấp thêm 2 trình xử lý là AuthSubTokenInfoAuthSubRevokeToken để quản lý mã thông báo. AuthSubTokenInfo rất hữu ích khi kiểm tra tính hợp lệ của mã thông báo. AuthSubRevokeToken cho phép người dùng ngừng truy cập vào dữ liệu của họ. Ứng dụng của bạn nên sử dụng AuthSubRevokeToken làm phương pháp hay nhất. Cả hai phương thức đều được hỗ trợ trong thư viện Ruby.

Cách truy vấn siêu dữ liệu của mã thông báo:

client.auth_handler.info

Cách thu hồi mã thông báo phiên:

client.auth_handler.revoke

Hãy xem tài liệu đầy đủ về Xác thực AuthSub cho các ứng dụng web để biết toàn bộ thông tin về AuthSub.

OAuth

Tại thời điểm viết bài viết này, OAuth chưa được thêm vào mô-đun GData::Auth.

Việc sử dụng OAuth trong thư viện tiện ích sẽ tương đối đơn giản khi bạn dùng oauth-plugin của Rails hoặc oauth gem của Ruby. Trong cả hai trường hợp, bạn đều muốn tạo một đối tượng GData::HTTP::Request và truyền cho đối tượng đó tiêu đề Authorization do mỗi thư viện tạo.

Truy cập vào nguồn cấp dữ liệu

GET (tìm nạp dữ liệu)

Sau khi thiết lập một đối tượng máy khách, hãy sử dụng phương thức get() của đối tượng đó để truy vấn một nguồn cấp dữ liệu của Google. Bạn có thể dùng XPath để truy xuất các phần tử Atom cụ thể. Sau đây là ví dụ về cách truy xuất Tài liệu của người dùng trên Google:

feed = client.get('http://docs.google.com/feeds/documents/private/full').to_xml

feed.elements.each('entry') do |entry|
  puts 'title: ' + entry.elements['title'].text
  puts 'type: ' + entry.elements['category'].attribute('label').value
  puts 'updated: ' + entry.elements['updated'].text
  puts 'id: ' + entry.elements['id'].text
  
  # Extract the href value from each <atom:link>
  links = {}
  entry.elements.each('link') do |link|
    links[link.attribute('rel').value] = link.attribute('href').value
  end
  puts links.to_s
end

POST (tạo dữ liệu mới)

Sử dụng phương thức post() của một ứng dụng để tạo dữ liệu mới trên máy chủ. Ví dụ sau đây sẽ thêm new_writer@example.com làm cộng tác viên vào tài liệu có mã nhận dạng: doc_id.

# Return documents the authenticated user owns
feed = client.get('http://docs.google.com/feeds/documents/private/full/-/mine').to_xml
entry = feed.elements['entry']  # first <atom:entry>

acl_entry = <<-EOF
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gAcl='http://schemas.google.com/acl/2007'>
  <category scheme='http://schemas.google.com/g/2005#kind'
    term='http://schemas.google.com/acl/2007#accessRule'/>
  <gAcl:role value='writer'/>
  <gAcl:scope type='user' value='new_writer@example.com'/>
</entry>
EOF

# Regex the document id out from the full <atom:id>.
# http://docs.google.com/feeds/documents/private/full/document%3Adfrk14g25fdsdwf -> document%3Adfrk14g25fdsdwf
doc_id = entry.elements['id'].text[/full\/(.*%3[aA].*)$/, 1]
response = client.post("http://docs.google.com/feeds/acl/private/full/#{doc_id}", acl_entry)

PUT (cập nhật dữ liệu)

Để cập nhật dữ liệu trên máy chủ, hãy sử dụng phương thức put() của ứng dụng. Ví dụ sau đây sẽ cập nhật tiêu đề của một tài liệu. Thao tác này giả định rằng bạn có một nguồn cấp dữ liệu từ một truy vấn trước đó.

entry = feed.elements['entry'] # first <atom:entry>

# Update the document's title
entry.elements['title'].text = 'Updated title'
entry.add_namespace('http://www.w3.org/2005/Atom')
entry.add_namespace('gd','http://schemas.google.com/g/2005')

edit_uri = entry.elements["link[@rel='edit']"].attributes['href']
response = client.put(edit_uri, entry.to_s)

DELETE

Để xoá một <atom:entry> hoặc dữ liệu khác khỏi máy chủ, hãy sử dụng phương thức delete(). Ví dụ sau đây sẽ xoá một tài liệu. Đoạn mã này giả định rằng bạn có một mục nhập tài liệu từ một truy vấn trước đó.

entry = feed.elements['entry'] # first <atom:entry>
edit_uri = entry.elements["link[@rel='edit']"].attributes['href']
client.headers['If-Match'] = entry.attribute('etag').value  # make sure we don't nuke another client's updates
client.delete(edit_uri)

Tạo ứng dụng Rails mới

Thông thường, bài tập đầu tiên trong việc tạo một ứng dụng Rails mới là chạy trình tạo giàn giáo để tạo các tệp MVC. Sau đó, hãy chạy rake db:migrate để thiết lập các bảng cơ sở dữ liệu. Tuy nhiên, vì ứng dụng của chúng ta sẽ truy vấn Google Documents List API để lấy dữ liệu, nên chúng ta không cần nhiều đến các cơ sở dữ liệu hoặc cấu trúc chung. Thay vào đó, hãy tạo một ứng dụng mới và bộ điều khiển đơn giản:

rails doclist
cd doclist
ruby script/generate controller doclist

và thực hiện các thay đổi sau đối với config/environment.rb:

config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
config.gem 'gdata', :lib => 'gdata'

Dòng đầu tiên huỷ liên kết ActiveRecord khỏi ứng dụng. Dòng thứ hai tải gem gdata khi khởi động.

Cuối cùng, tôi chọn kết nối tuyến đường mặc định ("/") với thao tác documents trong DoclistController. Thêm dòng này vào config/routes.rb:

map.root :controller => 'doclist', :action => 'all'

Khởi động bộ điều khiển

Vì chúng ta không tạo giàn giáo, hãy thêm một thao tác có tên là "all" vào DoclistController trong app/controllers/doclist_controller.rb theo cách thủ công.

class DoclistController < ApplicationController
  def all
    @foo = 'I pity the foo!'
  end
end

và tạo all.html.erb trong app/views/doclist/:

<%= @foo %>

Khởi động máy chủ web và bắt đầu phát triển

Giờ đây, bạn có thể bắt đầu máy chủ web mặc định bằng cách gọi ruby script/server. Nếu mọi thứ đều ổn, việc trỏ trình duyệt đến http://localhost:3000/ sẽ hiển thị "I pity the foo!".

Lưu ý: Đừng quên xoá hoặc đổi tên public/index.html.

Sau khi mọi thứ hoạt động, hãy xem DoclistControllerApplicationController cuối cùng của tôi để biết nội dung chính của dự án Trình quản lý danh sách tài liệu. Bạn cũng nên xem ContactsController. Lớp này xử lý các lệnh gọi đến API Danh bạ Google.

Kết luận

Phần khó nhất khi tạo ứng dụng Google Data Rails là định cấu hình Rails! Tuy nhiên, việc triển khai ứng dụng của bạn cũng quan trọng không kém. Vì vậy, bạn nên dùng mod_rails cho Apache. Bạn có thể dễ dàng thiết lập, cài đặt và chạy. Bạn sẽ có thể sử dụng ngay lập tức!

Tài nguyên

Phụ lục

Ví dụ

DocList Manager là một mẫu Ruby on Rails đầy đủ minh hoạ các chủ đề được thảo luận trong bài viết này. Bạn có thể xem toàn bộ mã nguồn trên trang lưu trữ dự án.