cookieを用いたセッションを書いてみました

webapp2などGAE標準添付のフレームワークにもセッションの機能は用意されているのですが今回は勉強の意味も込めてcookieを利用した簡易なものを書いてみました。
IDとパスワードを用いたログイン・ログアウトの機能を持つものではなく単に利用者を識別するだけのものです。

流れとしては最初にアクセスがあった際に

sessionID = binascii.hexlify(os.urandom(8))

でセッション用のID(文字列)を作成しcookieにセット、2度目以降のアクセスではcookieを見て利用者を識別します。

ページの機能はユーザー名と任意の単語を入力し、単語が入力されるたびそれらをリスト表示していくというものです。(下、スクリーンショット)
cookieの寿命を5分としているのでその間入力できることになります。


class UserProfile(db.Model):
    sessionID = db.StringProperty()
    name = db.StringProperty()
    words = db.StringListProperty()
    date = db.DateTimeProperty(auto_now_add=True)

上のようなエンティティを作成し、セッションID、ユーザー名、これまでに入力した単語のリスト、作成日時を保存しています。
cookieの寿命を5分に設定しているのに対しデータストアのエンティティには寿命がないため、時間切れで不要になったデータが溜まっていってしまいます。
これを定期的に削除するにはcronというのを使うとよさそうなのですが今回はそのまま放置です(^_^;)

# coding: utf-8

import webapp2
import cgi
import os
import binascii
import jinja2
from google.appengine.ext import db

jinja_environment = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))

DEFAULTUSER = {'name': 'GUEST', 'words': [u'ナシ']}


class UserProfile(db.Model):
    sessionID = db.StringProperty()
    name = db.StringProperty()
    words = db.StringListProperty()
    date = db.DateTimeProperty(auto_now_add=True)


class MainPage(webapp2.RequestHandler):
    @staticmethod
    def _session_check(sessionID):
        """
        セッションIDがすでにデータストアに登録されているか確認し
        Queryを返す
        """
        sessions = (UserProfile
                    .all()
                    .filter('sessionID =', sessionID)
                    )
        return sessions

    def _screenwrite(self, session_cookie=False, user=DEFAULTUSER):
        """
        HTMLを出力する
        session_cookie:新たにcookieを発行する場合のID
        user:データストアから取得した該当user
        """
        self.response.headers['Content-Type'] = 'text/html;charset=utf-8'

        if session_cookie:
            self.response.set_cookie('sessionID', session_cookie, max_age=300)

        template = jinja_environment.get_template('sessiontest.html')
        self.response.out.write(template.render(user=user))

    def get(self):
        sessionID = self.request.cookies.get('sessionID')

        if sessionID and self._session_check(sessionID):
            # すでにセッションがある場合はpostの場合を実行
            self.post()
            return

        #新しくIDを作成
        sessionID = binascii.hexlify(os.urandom(8))

        session = UserProfile(sessionID=sessionID)
        session.name = 'GUEST'
        session.words = []
        session.put()

        self._screenwrite(session_cookie=sessionID)

    def post(self):
        sessionID = self.request.cookies.get('sessionID')

        if not (sessionID and self._session_check(sessionID)):
            #sessionIDが未設定、もしくはデータストアにない場合はget()を実行
            self.get()
            return

        #該当するセッションを取得
        session = (self._session_check(sessionID))[0]

        session.name = cgi.escape(self.request.get('username')) or session.name
        if self.request.get('word'):
            session.words.append(cgi.escape(self.request.get('word')))

        session.put()

        self._screenwrite(user=session)

app = webapp2.WSGIApplication([('/', MainPage)],
                              debug=True)

htmlテンプレート

<!DOCTYPE HTML html>

<html>
<head>
	<title>セッションテスト</title>
</head>
<body>
	ようこそ{{ user.name }}さん<br>
	これまでの入力は
	<ul>
	{% for word in user.words %}
		<li>{{ word }}</li>
	{% endfor %}
	</ul>
	<br>
	<form action="/" method="post">
	名前を入力する<br>
	<input type="text" name="username"><br>
	単語<br>
	<input type="text" name="word"><br>
	<input type="submit">
	</form>
</body>
</html>