- 当我们进行启动项目的时候,首先需要的就是注册路由,所以我们可以定义一个方法,这个方法叫
get_handlers
,来获取整个项目的路由 - 但是如何表示一个类是一个路由呢?这个时候,我们就用到了装饰器
open_handler
,经过这个装饰器装饰的类,那么他就是一个路由,所以open_handler
至少有两个参数path
路径index
用于路由排序
装饰器
我们在目录tornado项目core\decorators\open_handler
下写一个装饰器,类作为装饰器 点击
class HandlerDecorate:
"""路由装饰器
1. 实质上路由装饰器只是给装饰的类添加了__url_path__的属性
2. 我们初始化的时候就通过__url_path__这个属性来判断当前的类是路由的处理器
3. __url_path_index__用来给路由进行排序
"""
def __init__(self, path, index=None) -> None:
self.__url_path__ = path
self.__url_path_index__ = index
def __call__(self, cls):
cls.__url_path__ = self.__url_path__
cls.__url_path_index__ = self.__url_path_index__
return cls
我们在__init__.py
中暴露这个装饰器
from core.decorators.open_handler.index import HandlerDecorate
open_handler = HandlerDecorate
只要经过@open_hander
修饰的类,就会含有__url_paht__
属性,如
@open_handler(path="/api/(.*)", index=1)
class openApiHandlerService(RequestHandler):
def get(self, *args, **kwargs):
self.write("hello world")
self.finish()
那么,openApiHandlerService就会含有__url_paht__
属性为/api/(.*)
路由的收集
定义get_handlers
方法,来获取被装饰器修饰的类
遍历项目文件
我们需要定义一个遍历项目文件的函数,当前函数用于查找项目中被
open_handler
修饰的类
我们在core\utils\module_utils.py
下,新建一个函数,用于遍历得到某文件下的所有的module
import importlib
import pkgutil
import os
def get_all_modules(path, file_name):
"""
获取路径下的所有file_name的module
"""
# 获取目录的配置(目录必须是一个包,才会有__path__属性)
_app = importlib.import_module(path)
def get_all_moudules_name(package_path, prefix=""):
# 用于深度遍历文件夹
for loader, name, is_package in pkgutil.iter_modules([package_path]):
if is_package:
# 如果还是一个包,那么再次遍历,且遍历的时候加上前缀
for _name in get_all_moudules_name(os.path.join(package_path, name), prefix=f"{name}."):
yield prefix + _name
else:
yield prefix + name
return [
importlib.import_module(f"{path}.{module_name}")
for module_name in get_all_moudules_name(_app.__path__[0])
if module_name.split(".")[-1][:len(file_name)] == file_name
]
为了规范,我们所有的路由定义的类都凡在开都为handlers
的文件中
获取项目下所有的路由
我们在core\decorators\open_handler
下新建一个OpenHandler
的类,并定义一个get_handlers
的方法用来获取所有的路由
class OpenHandler:
"""路由容器
1. 初始化的时候获取路由的容器类和工具类
2. 返回tornado路由列表
"""
def __init__(self, source_libs) -> None:
self.source_libs = source_libs # 传递目录名,用于获取目录下所有使用装饰器的类
self.handler_libs = [] # 存储路由的容器
def get_handlers(self):
self._handler_libs_ = []
_modules = [] # 目录下所有的moudule容器
for _path in self.source_libs:
_modules += get_all_modules(_path, "handler")
for _module in _modules:
self.handler_libs += [
dict(
path=_member.__url_path__,
handler=f"{_member.__module__}.{_member.__name__}",
index = _member.__url_path_index__
)
for _name,_member in inspect.getmembers(_module, inspect.isclass)
if hasattr(_member, "__url_path__")
]
# 排序
self.handler_libs.sort(key=lambda x: x['index'])
# 生成tornado的路由对象
_result = []
for _handler_lib in self.handler_libs:
# 遍历生成tornado的路由对象
_path = _handler_lib.get("path")
_result.append(
url(_path, import_object(_handler_lib.get("handler"))))
# 所有的路由列表
return _result
注册路由
一般我们在启动项目的时候需要注册路由
获取handler下的所有路由
为了项目规范我们把所有的路由都注册在了命名为
handler
的文件夹下,在启动环境的时候,我们一般调用Environment
类下的方法,我们在Environment
下,写一个获取handler下的路由的方法
class Environment:
...
def get_all_handlers(self):
open_handler = OpenHandler(["core"])
return open_handler.get_handlers()
他所表示的是获取core
文件夹下的所有的路由,然后我们进行路由的注册
class Application(Application):
def __init__(self):
# 初始化数据库、redis等,项目启动的时候,不要引入模型类
# 注册路由
handlers = environment.get_all_handlers()
# 添加动态路由
super().__init__(handlers, **config.settings)
测试
- 启动项目,我们输入
http://localhost:8888/api/
,就会发现我们可以访问到注册的路由了