GAEでREST的なGETを行う−URLルーティング方法2種
たとえば、検索条件に従って検索を行うhogeというページがあったとすると、検索条件をサーバ側に送信する方法には次のようなものがあるだろう。
方法1
GETメソッドで、URLにパラメータを付与する。
http://サーバアドレス/hoge?param1=xxxx¶m2=yyyy¶m3=zzzz
方法2
検索条件をformタグとinputタグまたはhiddenタグでくくっておき、POSTメソッドでページごと送り、サーバ側でリクエストボディから引数を取得する。
方法3
ページの遷移先をリソースパスとして指定する
http://サーバアドレス/hoge/xxxx/yyyy/zzzz
このうち、方法2は行儀がわるいやり方なので今回は割愛。
GAEにおける、方法1と方法3のやり方を比較してみる。
基本形:GET+URLパラメータ
まず、方法1のやり方を基本形とする。このようなコードになるだろう。
URL
http://サーバアドレス/hoge?param1=xxxx¶m2=yyyy¶m3=zzzz
app.yaml
- url: /hoge script: main.app
main.py
app = webapp2.WSGIAppliation([ ("/hoge", "handler.HogeHandler") ], debug=True)
handler.py
class HogeHandler(webapp2.RequestHandler): def get(self): param1 = cgi.escape(self.request.get("param1")) param2 = cgi.escape(self.request.get("param2")) param3 = cgi.escape(self.request.get("param3")) ...
これをふまえ、方法3のルーティングに対応させるにはどうすればよいか。
仮に、何も変更せずに、.../hoge/xxxx/yyyy/zzzzというURLを指定したとすると、.../hoge/xxxx/yyyy/zzzz というURLはパターンマッチしないため、HTTP 404エラーとなる。
さてどうするか。
解答1:正規表現でパターンマッチさせる
hogeの次に指定されるパスが3つと決まっているならば、まずmain.pyをこのように改変する。
main.py
app = webapp2.WSGIAppliation([ ("/hoge", "handler.HogeHandler"), ("/hoge/(.*)/(.*)/(.*)", "handler.HogeHandler"), ], debug=True)
ハンドラでは、スラッシュで区切られたデータを次のように受け取ることができる。
handler.py
class HogeHandler(webapp2.RequestHandler): def get(self, param1, param2, param3): logging.debug(param1) # xxxx が入ってる logging.debug(param2) # yyyy が入ってる logging.debug(param3) # zzzz が入ってる
ハンドラでの受け取り方はもう1つある。
handler.py
class HogeHandler(webapp2.RequestHandler): def get(self, *args, **kwargs): logging.debug(arg) # (xxxx, yyyy, zzzz) param1 = args(0) param2 = args(1) param3 = args(2)
つまり、*argsに配列として/hoge配下の各パスが格納されてくる。
前者は、パスの階層の数が固定となってしまうデメリットがある。
後者は、main.pyの正規表現を、何階層でもOKなように変更してやれば、任意の階層の数に対応することができるメリットがある一方、何番目に何が入っているかをわかっていなければならない(args(n)のnがマジックナンバーになる)というデメリットがある。
解答2:webapp2.Routeを使う
main.py
app = webapp2.WSGIAppliation([ webapp2.Route("/hoge", handler="handler.HogeHandler"), webapp2.Route("/hoge/<param1>/<param2>/<param3>", handler="handler.HogeHandler"), ], debug=True)
handler.py
class HogeHandler(webapp2.RequestHandler): def get(self, *args, **kwargs): logging.debug(arg) # () logging.debug(kwarg) # {'param1': 'xxxx', 'param2': 'yyyy', 'param3': 'zzzz'} param1 = kwargs['param1'] param2 = kwargs['param2'] param3 = kwargs['param3']
kwargsに辞書として格納される。
解答1よりも解答2のやり方のほうが好きだな。
補足:同じURIに対して、GETパラメータも受け取りたい、リソースパスも受け取りたい、のとき
パターン1:
/hoge?param0=1 (GETメソッドで)
パターン2:
/hoge/xxxx/yyyy (GETメソッドで)
を両立させたかったので、次のようにやってみたが、
main.py
app = webapp2.WSGIAppliation([ webapp2.Route("/hoge", handler="handler.HogeHandler"), #パターン1をルーティングさせたい webapp2.Route("/hoge/<param1>/<param2>/<param3>", handler="handler.HogeHandler"), #パターン2をルーティングさせたい ], debug=True)
これだと、パターン1がルーティングしてくれない。正規表現をゴニョゴニョすれば解決するのかもしれないが、次のように変更することでルーティングに成功した。
main.py
app = webapp2.WSGIAppliation([ ("/hoge", "handler.HogeHandler"), #パターン1のルーティング webapp2.Route("/hoge/<param1>/<param2>/<param3>",handler="handler.HogeHandler"), #パターン2のルーティング ], debug=True)