装饰器
装饰器定义在decorators.py中,其中最重要的就是expose,它标识了这个被装饰的方法可以被路由找到
def when_for(controller):
def when(method, **kw):
def decorate(f):
_cfg(f)['generic_handler'] = True
controller._pecan['generic_handlers'][method.upper()] = f
controller._pecan['allowed_methods'].append(method.upper())
expose(**kw)(f)
return f
return decorate
return when
// template:标识了渲染模板,默认moko,
// generic:默认的方法处理所有类型的请求(GET,POST,PUT,DELETE),如果为true,则各种类型的请求分开处理
// route:自定义路由
def expose(template=None,
generic=False,
route=None,
**kw):
content_type = kw.get('content_type', 'text/html')
if template == 'json':
content_type = 'application/json'
def decorate(f):
# flag the method as exposed
f.exposed = True
cfg = _cfg(f)
cfg['explicit_content_type'] = 'content_type' in kw
if route:
# This import is here to avoid a circular import issue
from pecan import routing
if cfg.get('generic_handler'):
raise ValueError(
'Path segments cannot be overridden for generic '
'controllers.'
)
routing.route(route, f)
# set a "pecan" attribute, where we will store details
cfg['content_type'] = content_type
cfg.setdefault('template', []).append(template)
cfg.setdefault('content_types', {})[content_type] = template
# handle generic controllers
if generic:
if f.__name__ in ('_default', '_lookup', '_route'):
raise ValueError(
'The special method %s cannot be used as a generic '
'controller' % f.__name__
)
cfg['generic'] = True
cfg['generic_handlers'] = dict(DEFAULT=f)
cfg['allowed_methods'] = []
// 方法可以被类似@index.when()装饰
f.when = when_for(f)
# store the arguments for this controller method
// 参数
cfg['argspec'] = getargspec(f)
return f
return decorate
1.cfg = _cfg(f)代码为方法指定了参数_pecan,dict对象,其中存储了该方法很多重要信息
2. cfg[‘generic_handlers’] = dict(DEFAULT=f) 当generic为true时,其中存储了具体的处理方法,{‘generic_handlers’:{‘DEFAULT’:function,‘POST’:function,‘PUT’:function}},当请求时POST,PUT,DELETE等方式时,就是从其中获取处理方法
3. f.when = when_for(f)
// 看似是对index_post方法进行装饰,但是主要还是对index方法进行处理
// 这里把index_post方法添加到index方法的_pecan['generic_handlers']中
// 这个写法很有意思,大家可以借鉴
@index.when(method='POST')
def index_post(self):
pass
根据POST,PUT,DELETE路由
在routing.py中find_object方法会返回找到的subcontroller,它是有@expose装饰的一个方法
def find_object(obj, remainder, notfound_handlers, request):
...
...
# Last-ditch effort: if there's not a matching subcontroller, no
# `_default`, no `_lookup`, and no `_route`, look to see if there's
# an `index` that has a generic method defined for the current request
# method.
if not obj and not notfound_handlers and hasattr(prev_obj, 'index'):
if request.method in _cfg(prev_obj.index).get('generic_handlers',
{}):
return prev_obj.index, prev_remainder
在core.py中根据POST具体找到相应的方法
def find_controller(self, state):
...
...
if cfg.get('generic'):
im_self = six.get_method_self(controller)
handlers = cfg['generic_handlers']
// 根据POST找到处理方法
controller = handlers.get(req.method, handlers['DEFAULT'])
handle_security(controller, im_self)
cfg = _cfg(controller)
所以最终找到处理方法是在core.py中,其实这里我认为处理的不好,还是应该在routing.py中处理
这里有几个写的不好的地方:
1.当请求为/v1//books这种不标准的形式的时候,pecan的路由机制是没法处理的
try:
next_obj, rest = remainder[0], remainder[1:]
// 可以将这里改成 if next_obj == '' and not rest 解决该问题
if next_obj == '':
index = getattr(obj, 'index', None)
if iscontroller(index):
return index, rest
def route(self, req, node, path):
// 可以将list中最后的空字符串删除,这个需和第一点配合
path = path.split('/')[1:]
3.对于 POST /v1/books/safgrgfwsfrsg (最后的一个segment没有定义,并且没有定义_lookup和_default),这是依然能够找到路由方法,但是会在参数处理的时候报错,这个地方不合理
def find_object(obj, remainder, notfound_handlers, request):
...
...
try:
obj = getattr(obj, next_obj, None)
except UnicodeEncodeError:
obj = None
// 添加这段代码规避该问题
###########
if not obj and not notfound_handlers and remainder:
abort(404)
##########
# Last-ditch effort: if there's not a matching subcontroller, no
# `_default`, no `_lookup`, and no `_route`, look to see if there's
# an `index` that has a generic method defined for the current request
# method.
if not obj and not notfound_handlers and hasattr(prev_obj, 'index'):
if request.method in _cfg(prev_obj.index).get('generic_handlers',
{}):
return prev_obj.index, prev_remainder
上面3点对源码的改动可以完成我们自定义的一些需求,并且是pecan的代码结构更加合理
(有兴趣的可以去github上提issue)