RESTで日本語 @ GoogleAppEngine
発表直後のGAEエントリーのピークは治まり、最近は、だんだGAEやり続けている人がポツポツと見え始めてきた気がする。
このままじゃ置いてきぼりになってしまうので、最近見つけたGAEネタをエントリーしてみる。
REST って必須だよね
デリシャスとかで、自分のブクマをタグで見たい時には
http://del.icio.us/ユーザー名/タグ名
的に書く。コレって意外と便利だし、タグが日本語だったとしても、safariだとエンコードされないからまんま見えて安心な感じ。ロケーションバーで日本語が見えるかどうかは重要じゃないが、RESTで日本語は使えないより使えた方がいいよね。
そんな、REST APIをGAEで作っているときに日本語が上手に取れない事が分かったので、それをどう解決するのかを書く。
まずはじめに
http://localhost:8080/get/日本語/
とリクエストされたら、”日本語”だけを取り出してみる。
>main.py
# coding:utf-8 #! /usr/bin/env python import wsgiref.handlers import urllib class Main(webapp.RequestHandler): def get(self): self.response.out.write("特に意味のないTopページです") class GetURI(webapp.RequestHandler): def get(self, uri_code): uri = urllib.unquote(urllib.unquote(uri_code)) self.response.out.write(uri.decode('utf-8')) def main(): app = webapp.WSGIApplication([ ('/tag/(.*)', GetURI), ('/', Main), ], debug=True) wsgiref.handlers.CGIHandler().run(app) if __name__ == '__main__': main()
main()の中のwebappの設定で、urlの所に(.*)と書けばそのクラスの引数になるポイ。
(.*)で取得出来た文言は、urllibのunquoteメソッドでアンクォートされるはずなんだけど、一度じゃだめで、二度unquoteする必要があった。とっても気持ち悪いけど、とりあえづ、こうしたら動いた。あとは、そのままout.writeすると、そのまま使える。
URIから取り出した文字列でフィルター出来ない、罠
URIから日本語取り出せても、そのまま表示に使っておしまいということはなく、たいていの場合は、その文言で検索やらフィルタすると思うが、なぜかここでもそのままではフィルタリング出来なかった。その解決方は以下の通り
(ハマった、簡単に説明しようと思ったけど、データの投稿部分がないと検証出来ない。とりあえづ、/post/hoge とすると hoge が登録されるとした。)
>main.py
# coding:utf-8 #! /usr/bin/env python import wsgiref.handlers from google.appengine.ext import db from google.appengine.ext import webapp import urllib # モデル strというカラムだけ持つ class Data(db.Model): str = db.StringProperty(required = True) create_at = db.DateTimeProperty(auto_now_add = True) class Main(webapp.RequestHandler): def get(self): self.response.out.write("特に意味のないTopページです") # 渡されたURIを元にDataStoreにフィルターをかけるクラス class GetURI(webapp.RequestHandler): def get(self, uri_code): # unquoteを2回かけて日本語取り出す uri = urllib.unquote(urllib.unquote(uri_code)) val = {'Data':[i for i in Data.all().filter('str = ', uri.decode('utf-8'))]} self.response.out.write(val) # 渡されたURI文字列をDataStoreに登録するクラス class PostURI(webapp.RequestHandler): def get(self, uri_code): uri = urllib.unquote(urllib.unquote(uri_code)) b = Data(str = uri.decode('utf-8')) b.put() self.response.out.write("%sを書き込みました。"%(b.str.encode('utf-8'))) def main(): app = webapp.WSGIApplication([ ('/get/(.*)', GetURI), ('/post/(.*)', PostURI), ('/', Main), ], debug=True) wsgiref.handlers.CGIHandler().run(app) if __name__ == '__main__': main()
ケッツマづいたところ
- (.*)で引き取った文字列は urllib.unquote を2回かけないと日本語にならない
- Model.all() に .filter()かける時の引数は、カラム名と“=(イコール)”の間にスペースが必要 例) .filter('hoge =', foo)
- 日本語でフィルターかける時には、.decode('utf-8')をつける
なんか、ぜんっぜん間違ったやり方ポイケド、とりあえづ動いているのでよしとする。
もっと正しくて簡単なやり方あったら教えてください。
おまけ
コピペで動かせるように app.yamlを張っておく
>app.yaml
application: JapaneseRESTSample version: 1 api_version: 1 runtime: python handlers: - url: .* script: main.py
上記、main.pyとapp.yamlを同じフォルダーに保存(utf8,lf)して、そのディレクトリー内で
$ dev_appserver.py .
とやってもらえれば、動くと思う。
まだ、GoogleAppEngineを入手されてない方は、http://code.google.com/appengine/downloads.html
ここに行けば、Win OSX LinuxのSDKが落とせますです。
おまけ2
getメソッドで、self.request._environするといろいろとれる。
始めは、(.*) 知らなくて、_environ から無理矢理splitしてた。
ブラウザとかOSとかも取れるからなにかに使えるかも・・
今の環境だとこうなった
{ 'SERVER_SOFTWARE': 'Development/1.0', 'SCRIPT_NAME': '', 'REQUEST_METHOD': 'GET', 'HTTP_KEEP_ALIVE': '300', 'SERVER_PROTOCOL': 'HTTP/1.0', 'QUERY_STRING': '', 'CONTENT_LENGTH': '', 'HTTP_ACCEPT_CHARSET': 'Shift_JIS,utf-8;q=0.7,*;q=0.7', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X; ja-JP-mac; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14', 'TZ': 'UTC', 'SERVER_NAME': 'localhost', 'REMOTE_ADDR': '127.0.0.1', 'wsgi.url_scheme': 'http', 'PATH_TRANSLATED': '********************************************', 'SERVER_PORT': '8080', 'AUTH_DOMAIN': 'gmail.com', 'wsgi.input': , 'HTTP_HOST': 'localhost:8080', 'wsgi.multithread': False, 'HTTP_CONNECTION': 'keep-alive', 'USER_EMAIL': '', 'HTTP_ACCEPT': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5', 'wsgi.version': (1, 0), 'GATEWAY_INTERFACE': 'CGI/1.1', 'wsgi.run_once': True, 'wsgi.errors': ', mode 'w' at 0x170b0>, 'wsgi.multiprocess': True, 'HTTP_ACCEPT_LANGUAGE': 'ja,en-us;q=0.7,en;q=0.3', 'CONTENT_TYPE': 'application/x-www-form-urlencoded', 'wsgi.file_wrapper': , 'HTTP_ACCEPT_ENCODING': 'gzip,deflate', 'PATH_INFO': '***********************' }