第2节,tornado 路由系统,建立请求path与RequestHandler之间的映射关系

路由系统是每一个python web框架都要提供的功能,tornado也不例外,虽然各自实现上有说不同,但本质上都是在建立请求path和处理该类请求代码(函数,类)之间的映射关系。

1. 路由简单示例

import tornado.ioloop
from tornado.web import RequestHandler, Application
from tornado.httpserver import HTTPServer
from tornado.options import options, define

define('port', default=8000, help='监听端口')


class HelloHandler(RequestHandler):
    def get(self):
        self.write('hello world')

class IndexHandler(RequestHandler):
    def get(self):
        self.write('welcome to IndexHandler')

if __name__ == '__main__':
    options.parse_command_line()
    handlers_routes = [
        (r'/', HelloHandler),
        (r'/index', IndexHandler)
    ]
    app = Application(handlers=handlers_routes)
    http_server = HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.current().start()

在创建Application对象时,需要设置handlers参数,如上代码所示,handlers_routes 是一个列表,列表里的元素是元组,元素第一个元素是正则表达式,描述的是请求的path,第二个元素是RequestHandler类,处理path符合前面正则表达式的请求。

2. 动态路由

tornado 也支持动态路由,需要你了解一点正则表达式分组的知识

class UserHandler(RequestHandler):
    def get(self, name):
        self.write(f'welcome {name}')
        

if __name__ == '__main__':
    options.parse_command_line()
    handlers_routes = [
        (r'/', HelloHandler),
        (r'/index', IndexHandler),
        (r'/user/(.*)', UserHandler)
    ]

我只贴出来部分修改新增的代码,r'/user/(.*)' 是一个正则表达式,(.*) 是一个分组,其匹配到的内容将传递给UserHandler类的get方法中的name参数。正则表达式里有几个分组,在对应的处理请求的方法里就要有几个参数。

下面的正则表达式也成立,而且代码更利于阅读,你可以根据正则表达式里的捕获分组准确的理解path里每一个分组的含义。

(r'/user/(?P<name>.*)', UserHandler)

关于正则表达式分组,如果你并不十分熟练,推荐你阅读python正则表达式精讲--分组

3. URLSpec

偶尔,你也会看到这样的写法

from tornado.routing import URLSpec

handlers_routes = [
        (r'/', HelloHandler),
        URLSpec(r'/index', IndexHandler),
        (r'/user/(?P<name>.*)', UserHandler)
    ]

可以使用URLSpec类来确定路径和处理请求类之间的映射关系,这与使用元组在效果上是相同的。使用元组,tornado会根据元组里的数据生成Rule对象,而URLSpec是Rule的子类。

4. add_handlers

Application 类的add_handlers方法允许你自由添加路由规则,不仅如此,还可以向指定的host进行添加。

app = Application(handlers=handlers_routes)
    app.add_handlers(r'.*', [(r'/user/(?P<name>.*)', UserHandler)])

add_handlers的第一个参数是host_pattern,根据host_pattern生成Matcher对象,只有请求头里的host匹配了host_pattern才会生效,r'.*' 表示任意字符重复0次或多次,任意host都可以使用这些路由规则。app = Application(handlers=handlers_routes) 这种写法在源码里生成的是AnyMatches对象,同r'.*'一样,匹配任意host。

如果你的web服务有多个域名,而且希望不同的域名有不同的路由规则,那么你可以使用add_handlers方法来实现,在/etc/hosts里配置如下域名解析

127.0.0.1 mycool.com
127.0.0.1 mypythonweb.com

修改web应用

app = Application(handlers=handlers_routes)
    app.add_handlers(r'mycool.com', [(r'/user/(?P<name>.*)', UserHandler)])

访问http://mycool.com:8000/user/dongsheng 可以得到正确响应,而访问http://mypythonweb.com:8000/user/dongsheng ,得到的就是一个404 not found, host_pattern 可以匹配mycool.com,但不能匹配mypythonweb.com,因此(r'/user/(?P.*)' 对于mypythonweb.com是不可见的。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案