Tornadoを使ってみる

URLパス変数とURLパラメータ


URLパス変数というのはURLの後ろに/で続けて記載する変数です。
http://192.168.10.15:8000/test1/hoge
のhogeがパス変数になります。

URLパラメータというのはURLの後ろに?で続けて記載する変数で「?名前=値」の書式です。
http://192.168.10.15:8000/test2?path=hoge
のpathがURL変数名、hogeがその値となります。

Tornadoではどちらも扱うことができます。
# -*- coding: utf-8 -*-
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
#from tornado.web import url

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

# http://localhost:8000
class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        print("IndexHandler:get")
        self.write("Hello World")

# URLパス変数
# http://localhost:8000/test1/hoge
class Test1Handler(tornado.web.RequestHandler):
    def get(self, path):
        print("path={} len={}".format(path, len(path)))
        self.write("test1 path={} len={}".format(path, len(path)))

# URLパラメータ
# http://localhost:8000/test2?path=hoge
class Test2Handler(tornado.web.RequestHandler):
    def get(self):
        path = self.get_argument('path', None)
        print("path={}".format(path))
        self.write("test2 path={}".format(path))

'''
def make_app():
    return tornado.web.Application([
        (r"/", IndexHandler),
        (r"/test1/(.*)", Test1Handler),
        (r"/test2", Test2Handler),
    ],debug=True,)
'''

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[
            (r"/", IndexHandler),
            (r"/test1/(.*)", Test1Handler),
            (r"/test2", Test2Handler),
        ],debug=True
    )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.current().start()



URLパス変数を使う場合、Tornadoでは正規表現を使ってURLパス変数とクラスの対応を行います。
URLパス変数はget()の引数で渡ってきます。

正規表現に(.*)を指定すると、どのような文字もパス変数となりますが
http://192.168.10.15:8000/test1/
は通りますが、
http://192.168.10.15:8000/test1
はエラーになります。





また、階層構造を持つパス変数を使うことができます。
flaskでは階層構造を持つパス変数は使えないので、flaskに移植するときは注意が必要です。




URLパラメータは省略時のデフォルトを指定することができます。
複数のURLパラメータを使う場合、全てのパラメータを指定する必要は有りません。
省略されたパラメータはデフォルト値が使われます。




URLパラメータの省略時のデフォルトをget_argument関数で指定することができます。
デフォルト値は文字列や数値で指定することができますが、数値で指定した場合、少し困ったことが起こります。
# URLパラメータ
# http://localhost:8000/test2?path=hoge
class Test2Handler(tornado.web.RequestHandler):
    def get(self):
        path = self.get_argument('path', 0)
        print("path={} type={}".format(path, type(path)))
        self.write("test2 path={}".format(path))

URLパラメータに文字が指定された場合、このようなExceptationが起こります。
[E 210523 17:06:26 web:1793] Uncaught exception GET /test2?path=AA (192.168.10.107)
    HTTPServerRequest(protocol='http', host='192.168.10.43:8000', method='GET', uri='/test2?path=AA', version='HTTP/1.1', remote_ip='192.168.10.107')
    Traceback (most recent call last):
      File "/home/nop/.local/lib/python3.6/site-packages/tornado/web.py", line 1702, in _execute
        result = method(*self.path_args, **self.path_kwargs)
      File "./parameter.py", line 30, in get
        if (type(path) is str): path=int(path)
    ValueError: invalid literal for int() with base 10: 'AA'
[E 210523 17:06:26 web:2243] 500 GET /test2?path=AA (192.168.10.107) 2.40ms

これはtryで回避することができますが、さらに厄介な問題が有ります。
URLパラメータが省略されたときはint型の変数となり、URLパラメータに数値が指定されたときはstr型の変数となります。
以下の様にどちらもpath=0で、数値に見えますが型が違います。
path=0 type=<class 'int'>
[I 210523 17:09:57 web:2243] 304 GET /test2 (192.168.10.107) 0.68ms
path=0 type=<class 'str'>
[I 210523 17:09:48 web:2243] 304 GET /test2?path=0 (192.168.10.107) 1.07ms


python3では変数の型を厳密に扱うので、これ以降path変数を正しく扱うことができません。
変数の型を見て型変換する必要が有ります。
いろいろ厄介なので、URLパラメータ省略時のデフォルトはNoneにしておいた方が楽ちんです。
# URLパラメータ
# http://localhost:8000/test2?path=hoge
class Test2Handler(tornado.web.RequestHandler):
    def get(self):
        path = self.get_argument('path', None)
        if (type(path) is str): path=int(path)
        print("path={} type={}".format(path, type(path)))
        self.write("test2 path={}".format(path))

続く...