WebアプリからGoogle Tasks APIにアクセスする
Google Tasks APIはタスクにアクセスするためのAPIです。GoogleカレンダーにTo Doリストが付いていますよね、あれです。
WebアプリからGoogle Tasks APIにアクセスするにはクライアントサイド(JavaScript)とサーバサイドの2つの方法がありますが、今回はサーバサイドを説明します。
必要なもの。
- HTTPSをサポートするWebサーバ
- アプリを実行するAPサーバ
- ここではJava ServletベースのWebアプリで説明します。
- ところどころGoogle App Engine+Slim3なコードが出てきますがご了承ください。
- google-api-services-tasks クライアントライブラリ
- 依存関係が複雑なのでMavenを使うと便利です。
事前準備として、Google API ConsoleのTasks APIにアクセスしてClient IDとClient Secretを取得します。また、リダイレクトURLのホワイトリストを指定しておきます。リダイレクトURLはlocalhostも入れておきましょう。
おおまかな流れを以下に示します。
- アプリのトップページが表示される。
- ユーザがログインリンクをクリックすると、Googleの認可画面が表示される。
- ユーザがリソースへのアクセスを許可すると、アプリにリダイレクトされる。
- アプリはGoogleからトークンを取得し、セッションに保存する。
- アプリはTasks APIからデータを取得し、ページを返す。
Step1:トップページの表示
トップページは静的なHTMLで構いません。ここでは https://www.example.com/ とします。
トップページにはログインリンクを配置します。正確には、認可要求URLへのリンクです。リンク先は https://accounts.google.com/o/oauth2/auth に以下のパラメータを付けたものになります。詳しくはGoogle APIsの説明に書いてあります。
パラメータ名 | 値 |
---|---|
redirect_uri | リダイレクト先のURLを指定します。 |
client_id | Client IDを指定します。 |
scope | アクセス対象のリソースを指定します。ここではユーザのタスクを読み書きしたいので、 https://www.googleapis.com/auth/tasks を指定します。 |
response_type | codeを指定します。 |
<a class="session-login" href="https://accounts.google.com/o/oauth2/auth?redirect_uri=https://www.example.com/&response_type=code&scope=https://www.googleapis.com/auth/tasks&client_id=xxxCLIENTxIDxxx.apps.googleusercontent.com"> Googleアカウントでログイン </a>
サーバサイドでURLを生成することも可能です。
String redirectURL = ...; // リダイレクト先のURLを組み立てる String authorizationURL = new GoogleAuthorizationRequestUrl( Constants.clientCredential.getClientId(), // Client ID redirectURL, "https://www.googleapis.com/auth/tasks") .build(); // TODO: authorizationURLをJSPに投げる
クライアントサイドだとこんな感じ。
$('a.session-login').attr('href', 'https://accounts.google.com/o/oauth2/auth' + '?redirect_uri=' + location.protocol + '//' + location.host + location.pathname + '&response_type=code' + '&scope=https://www.googleapis.com/auth/tasks' + '&client_id=xxxCLIENTxIDxxx.apps.googleusercontent.com');
Step3:リダイレクト
ユーザがアクセスを許可した場合は認可コードが発行され、Step1で指定したURLにリダイレクトされます。
ユーザがアクセスを拒否した場合はerrorパラメータ付きでリダイレクトされます。
Step4:トークンの取得
リダイレクト先のサーブレットでは、認可コードからアクセストークンを取得します。アクセストークンの取得はクライアントライブラリの GoogleAuthorizationCodeGrant がやってくれます。具体的にはこんな感じ。
// https://www.example.com/callback のサーブレット // TODO: errorパラメータが付いている場合はトップページに戻す String authorizationCode = asString("code"); String redirectURI = ...; // Step1で指定したリダイレクト先 GoogleAuthorizationCodeGrant grant = new GoogleAuthorizationCodeGrant( NetHttpTransportLocator.get(), // NetHttpTransportのシングルトン JacksonFactoryLocator.get(), // JacksonFactoryのシングルトン Constants.clientCredential.getClientId(), // Client ID Constants.clientCredential.getClientSecret(), // Client Secret authorizationCode, redirectURI); AccessTokenResponse tokenResponse = execute(grant); // Serializableに入れてセッションに保存 CachedToken token = new CachedToken(tokenResponse.accessToken, tokenResponse.refreshToken);
なお、サーブレットが処理している間はWebブラウザが空白表示になります。リダイレクト先をHTMLにしておいてAjaxでサーブレットを叩くようにすると、体感的な待ち時間の短縮が期待できます。
var authorizationCodeMatch = location.search.match(/\?code=(.*)/); if (authorizationCodeMatch) { // セッションを開始する $.post('/callback', {code: authorizationCodeMatch[1]}, function () { location.replace(location.pathname); }); } else if (location.search == '?error=access_denied') { location.replace(location.pathname); } else if (...) { // セッションがすでに存在する場合
Step5:リソースへのアクセス
Tasks APIにアクセスするにはクライアントライブラリのTasksクラスを使います。同名のクラスが2つあって非常に紛らわしいため、スコープが重複しないように使った方がよいでしょう。
- com.google.api.services.tasks.Tasks はサービスを利用するためのクラス
- com.google.api.services.tasks.model.Tasks はモデルを表現するクラス
事前準備を前処理として切り出します。抽象クラスのController#setUp()に入れておくなど。
public abstract class ControllerBase extends Controller { protected Navigation setUp() { // サービスを利用する準備 CachedToken token = ...; // セッションから取り出す HttpTransport httpTransport = NetHttpTransportLocator.get(); JacksonFactory jsonFactory = JacksonFactoryLocator.get(); GoogleAccessProtectedResource resource = new GoogleAccessProtectedResource( token.getAccessToken(), httpTransport, jsonFactory, Constants.clientCredential.getClientId(), Constants.clientCredential.getClientSecret(), token.getRefreshToken()); this.tasksService = new Tasks(httpTransport, resource, jsonFactory);
モデルを扱う部分では、サービスのTasksに依存しないようにします。上記を実装したクラスを継承するなど。
public class ListController extends ControllerBase { public Navigation run() throws Exception { // Tasks APIにアクセスする String tasklistID = asString("tasklistID"); Tasks tasks = this.tasksService.tasks.list(tasklistID).execute();
クライアントライブラリの使い方は Write your First App: Prerequisites - Google Apps Platform — Google Developers で詳しく説明されています。タスク管理のモデルは奥が深いですね。