2010-10-08

GAE アプリでユーザごとの設定を可能にする #1

2010-10-08 時点の Blogger Glass では、アプリ固有の設定(例: スタイルシートの URL やブログ ID 等)は config.yaml というファイルに記述することになっている。

この方法の利点は、何と言ってもその手軽さにある。YAML (のサブセット)で書かれたファイルを読み込むのは単純で、ハードコードしたくない情報を簡単にプログラムから切り離すことができる。一方、欠点はと言えば、設定の変更にはアプリの再配備が必要なこと。プロトタイプとしてはこれでも悪くないが、ある程度アプリの機能が成熟してきたら、設定自体をアプリの機能として提供すべきだ。

そこで、Blogger Glass へ次に 追加する機能として「設定(Settings)」を選んだ。ブログ ID を「設定」で変更できるようになれば、LOG+REPO 以外のブログ(ただし Blogger で作ったものに限られる)を見られるようになる。

原則として個人が一人で使う iPhone のようなデバイス上のアプリとは異なり、ウェブアプリの設定は、ユーザごとに設定・保存できるものでなければならない。みんなが同じ URL でアクセスする以上、アプリはすべてのユーザが共有する。設定をユーザごとのものにしなければ、全ユーザが共有することになってしまう。そして、ユーザごとの設定にはユーザの識別が必要になる。

「設定」機能のデザイン(意匠と設計)に入る前に、GAE におけるユーザの識別について調べてみよう。

ユーザの識別

GAE アプリで、アプリを使っているユーザの識別するには GAE が提供する「ユーザーサービス」を利用する。

(「ユーザー サービスの使用」より)
サービスの 1 つであるユーザー サービスは、アプリケーションと Google ユーザー アカウントを連携させるものです。ユーザー サービスでは、ユーザーはすでに持っている Google アカウントを通じて、アプリケーションにログインすることができます。

ログインとログアウト

アプリを利用するユーザにログインを促す方法の 1 つは、リクエストハンドラで users.get_current_user() を使い、ユーザがログイン中であることを確認することだ。そしてログイン中でなければログイン画面にリダイレクトする。

(ユーザーサービス利用例)
from google.appengine.api import users 
[...snip...]
class MainHandler(webapp.RequestHandler):
    def get(self):
        user = users.get_current_user()
        if user:
            [...snip...]
            self.response.out.write("<div><a href='"
                                    + users.create_logout_url(self.request.uri)
                                    + "'>logout</a></div>")
        else:
            self.redirect(users.create_login_url(self.request.uri))

ログアウトに関しては、アプリ画面のどこかにログアウト用のリンクを置けば良い。これには users.create_logout_url()を使う。↑の例では、ログイン中であれば(つまり users.get_current_user() が有効なオブジェクトを返せば)、レスポンスにログアウト用の URL を出力している(アプリ画面の一部になる)。この例の場合、ログアウト用のリンクをクリックすると、(a) ログアウトが実行され、(b) このハンドラを起動した URL にリダイレクトされ、(c) ログイン中ではないからログイン画面にリダイレクトされ、(d) ログイン画面が表示されることになる。

特定の URL に対してログインを要求する

app.yaml に記述することで、特定のリクエスト(URL のパターン)に対してログインを強制することもできる。

(app.yaml の handlers 区画の記述サンプル)
handlers:
- url: /hello/
  script: hello.py
  login: required

- url: .*
  script: main.py

この設定では、/hello/ を開こうとすると、ログイン画面にリダイレクトされ、ログインしない限り、hello.py ハンドラが実行されることはない。

ハンドラ内でログイン画面にリダイレクトする方法と、app.yaml でリクエスト自体にログイン要求を設定する方法とは適宜使い分けることになる。たとえば、ログインをしなくとも使える機能(のハンドラ)についてはハンドラ内でログイン状態を識別し、必要に応じてリダイレクトする。一方、ログインしている状態でのみ使える機能(のハンドラ)に対しては、app.yaml で制限をかける。

Blogger Glass の場合だと、一覧表示や内容表示はログインしていなくても使えるが、新しく追加しようとしている「設定」はログインが必須になる。だから、前者はハンドラ内で識別とリダイレクトを記述し、後者にはapp.yaml でログインを必須に設定することになる。

User オブジェクト

実際のユーザの識別には User オブジェクトを利用する。

(「Google アカウント Python API 概要」より)
User オブジェクトからは、ユーザー アカウントが存在する限り、メール アドレスが変更されても一定である一意なユーザー ID を取得できます。この値は、データストアのエンティティ キーまたはプロパティ値として使用できます。

User.user_id() メソッドはユーザの一意で永続的な ID (型は str) を返す。

開発環境(SDK)でのログイン

ユーザーサービスを使ったアプリを開発するために、SDK にはログイン(とログアウト)をシミュレートするダミーのログイン画面が用意されている(→)。

上述の例のように self.redirect(users.create_login_url(self.request.uri)) でログイン画面にリダイレクトするとこの画面が表示され、ログインボタンを押すとアプリ画面に戻る。

次回予告

ログイン要求つきの設定画面(ビュー)を用意してユーザごとの項目を入力できるようになっても、それが保存できなければ意味がない。正確には、後続のリクエストを処理する際に参照できなければ役に立たない。つまり、リクエストハンドラ間で情報を共有する仕組みが必要になる。

そのための一番確実な方法は「データストア」を利用することだろう。ユーザごとの設定情報を永続化してしまえば、どのリクエストハンドラからも参照できる。情報を保存するハンドラと、参照するハンドラが別のサーバで実行されていたとしても問題ない。

次回は、GAE の提供する「データストア」の使い方を調べることから始める。

関連リンク

関連記事

0 件のコメント:

コメントを投稿