Basic認証付きデータストアをGoogle App Engine上に
Basic認証を用いて,Google App Engine上に任意のデータをアップロード,ダウンロードできる仕掛けを作ってみた.このご時世,自前サーバを外にさらすのはいやなので,Googleさまにお任せしようと.
パスをデータの名前として扱い,ダウンロード時のContent-Typeはパスの拡張子で適当に決めている.本当はアップロード時にContent-Typeを設定してもらって,それを使うようにした方がいいのかもしれないが.データはBlobとして扱う.アップロード, ダウンロード,消去は同じURLに対して,PUT/GET/DELETEメソッドで行う.こういうのREST風っていうんでいいんですよね?
ユーザごとに別の名前空間としているので,ユーザAのhttp://example.com/dataと ユーザBのhttp://example.com/dataは別のデータとなる.
# -*- coding: utf-8 -*- import wsgiref.handlers from google.appengine.ext import webapp, db from google.appengine.api import users from google.appengine.ext.webapp import template from basicAuth import basicAuth import os import logging typeDict = {"txt": "text/plain", "htm": "text/html", "html": "text/html", "xml": "text/xml", "gif": "image/gif", "jpg": "image/jpeg", "jpeg": "image/jpeg", "png": "image/png", "doc": "application/msword", "pdf": "application/pdf"} def lookupType(path): try: ext = path.split(".")[-1].lower() return typeDict[ext] except KeyError: return "application/octet-stream" class Store(db.Model): """データモデルの定義""" path = db.StringProperty() user = db.StringProperty() data = db.BlobProperty() userDict = {"userA": "passA", "userB": "passB"} class MainPage(webapp.RequestHandler): def _fetch(self, path, user): """storeを取得する""" q = db.GqlQuery("SELECT * FROM Store WHERE path = :1 AND user = :2", path, user) return q.get() ####### GET METHOD @basicAuth(userDict, "simplestore") def get(self): logging.info('authenticated as ' + self.request.basic_user) store = self._fetch(self.request.path, self.request.basic_user) if store: # storeがあれば中身を出力 self.response.out.write(store.data) self.response.headers["Content-Type"] = \ lookupType(self.request.path) else: # なければエラー self.response.set_status(404) self.response.out.write('<body><h1>Not Found</h1></body>\n') ####### PUT METHOD @basicAuth(userDict, "simplestore") def put(self): store = self._fetch(self.request.path, self.request.basic_user) if store: # 既存のstoreがあればそれをアップデート logging.info("update %s for user %s" % (self.request.path, self.request.basic_user)) store.data = self.request.body store.put() else: # なければ新しく作成 store = Store(path = self.request.path, user = self.request.basic_user, data = self.request.body) store.put() self.response.set_status(200) ####### DELETE METHOD @basicAuth(userDict, "simplestore") def delete(self): store = self._fetch(self.request.path, self.request.basic_user) if store: # 既存のstoreがあればそれを削除 logging.info("deleting %s for user %s" % (self.request.path, self.request.basic_user)) store.delete() self.response.set_status(200) else: logging.info("failed to delete %s for user %s" % (self.request.path, self.request.basic_user)) self.response.set_status(404) self.response.out.write('<body><h1>Not Found</h1></body>\n') def main(): application = webapp.WSGIApplication([('/.*', MainPage)], debug = True) wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__": main()