定义RequestHandler
URL处理函数不一定是一个 coroutine ,因此我们用 RequestHandler() 来封装一个URL处理函数。
RequestHandler 是一个类,由于定义了 call() 方法,因此可以将其实例视为函数。
RequestHandler 目的就是从URL函数中分析其需要接收的参数,从 request 中获取必要的参数,调用URL函数,然后把结果转换为 web.Response 对象,这样,就完全符合 aiohttp 框架的要求:
class RequestHandler(object):
def init(self, app, fn):
self.app = app
self.func = fn
…
@asyncio.coroutine
def __call(self, request):
kw = … 获取参数
r = yield from self._func(**kw)
return r
再编写一个 add_route 函数,用来注册一个URL处理函数:
def addroute(app, fn):
method = getattr(fn, 'method', None)
path = getattr(fn, 'route', None)
if path is None or method is None:
raise ValueError('@get or @post not defined in %s.' % str(fn))
if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn):
fn = asyncio.coroutine(fn)
logging.info('add route %s %s => %s(%s)' % (method, path, fn._name, ', '.join(inspect.signature(fn).parameters.keys())))
app.router.add_route(method, path, RequestHandler(app, fn))
最后一步,把很多次 add_route() 注册的调用:
add_route(app, handles.index)
add_route(app, handles.blog)
add_route(app, handles.create_comment)
…
变成自动扫描:
# 自动把handler模块的所有符合条件的函数注册了:
add_routes(app, 'handlers')
add_routes() 定义如下:
def addroutes(app, modulename):
n = modulename.rfind('.')
if n == (-1):
mod = import(module_name, globals(), locals())
else:
name = module_name[n+1:]
mod = getattr(__import(module_name[:n], globals(), locals(), [name]), name)
for attr in dir(mod):
if attr.startswith(''):
continue
fn = getattr(mod, attr)
if callable(fn):
method = getattr(fn, 'method', None)
path = getattr(fn, 'route', None)
if method and path:
add_route(app, fn)
最后,在 app.py 中加入 middleware 、 jinja2 模板和自注册的支持:
app = web.Application(loop=loop, middlewares=[
logger_factory, response_factory
])
init_jinja2(app, filters=dict(datetime=datetime_filter))
add_routes(app, 'handlers')
add_static(app)