tornado是一个服务器框架,,里面有get、put、post、delete请求接口,如何给这些接口动态加上装饰器,比如针对每次调用,进行如:用户是否登陆的校验?如果自己手动进行增减,容易遗漏
tornado是一个服务器框架,,里面有get、put、post、delete请求接口,如何给这些接口动态加上装饰器,比如针对每次调用,进行如:用户是否登陆的校验?如果自己手动进行增减,容易遗漏
必须是元类,用来控制类的创建。
示例代码,假设登录后get_user
能返回用户名,即类B
的是没有登录的:
# -*- coding: utf-8 -*-
import functools
class HttpError(Exception):
pass
def logined(func):
@functools.wraps(func)
def _wrapper(self, *args, **kwargs):
if self.user:
return func(self, *args, **kwargs)
# Or, redirect.
raise HttpError(403)
return _wrapper
class MetaWrapWithLogin(type):
def __new__(cls, name, bases, attrs):
need_login = list()
if 'need_login' in attrs:
need_login.extend(list(attrs['need_login']))
for method in need_login:
if method in attrs:
attrs[method] = logined(attrs[method])
return type.__new__(cls, name, bases, attrs)
class Base(object):
@property
def user(self):
return self.get_user()
def get_user(self):
pass
def get(self):
raise HttpError(404)
def post(self):
raise HttpError(404)
class A(Base):
__metaclass__ = MetaWrapWithLogin
need_login = ['get', 'post']
def get_user(self):
return 'foo'
def get(self):
print('{0} get'.format(self.__class__.__name__))
def post(self):
print('{0} post'.format(self.__class__.__name__))
class B(Base):
__metaclass__ = MetaWrapWithLogin
need_login = ['post']
def get(self):
print('{0} get'.format(self.__class__.__name__))
def post(self):
print('{0} post'.format(self.__class__.__name__))
if __name__ == '__main__':
a = A()
a.get()
a.post()
print('\n{0}\n'.format('-' * 10))
b = B()
b.get()
b.post()
输出:
A get
A post
----------
B get
Traceback (most recent call last):
File "test.py", line 88, in <module>
b.post()
File "test.py", line 16, in _wrapper
raise HttpError(403)
__main__.HttpError: 403
然后按照这个思路自己改造吧。
装饰器版:
# -*- coding: utf-8 -*-
import functools
def login_decorator(func):
@functools.wraps(func)
def _wrapper(self, *args, **kwargs):
if hasattr(self, 'user') and self.user:
return func(self, *args, **kwargs)
# Or, redirect.
raise HttpError(403)
return _wrapper
def add_to_methods(decorator, *methods):
def wrap_method(cls):
for method in methods:
if hasattr(cls, method):
setattr(cls, method, decorator(getattr(cls, method)))
return cls
return wrap_method
class HttpError(Exception):
pass
@add_to_methods(login_decorator, 'post')
class A(object):
def get(self):
print('{0} get'.format(self.__class__.__name__))
def post(self):
print('{1} post'.format(self.__class__.__name__))
if __name__ == '__main__':
a = A()
a.get()
a.post()
输出:
A get
Traceback (most recent call last):
File "need.py", line 42, in <module>
a.post()
File "need.py", line 12, in _wrapper
raise HttpError(403)
__main__.HttpError: 403
3 回答3.1k 阅读✓ 已解决
2 回答1.9k 阅读✓ 已解决
2 回答1.3k 阅读✓ 已解决
2 回答1.8k 阅读✓ 已解决
4 回答1.9k 阅读
3 回答1.7k 阅读
1 回答1.4k 阅读✓ 已解决
tornado在RequestHandler中提供了 prepare和finish这样的方法。prepare在框架调用get/post/...这些方法之前调用。finish在最终写回response时由框架调用。所以可以利用这个实现类似 django中middleware的功能。比如登录检查,权限验证,修改response的结果都可以在middleware中完成。