天蓬老师2017-04-17 16:50:32
tornado在RequestHandler中提供了 prepare和finish這樣的方法。 prepare在框架呼叫get/post/...這些方法之前呼叫。 finish在最終寫回response時由框架呼叫。所以可以利用這個實作類似 django中middleware的功能。例如登入檢查,權限驗證,修改response的結果都可以在middleware中完成。
class Middleware(object):
def process_request(self, handler):
pass
def process_response(self, handler):
pass
def prepare(self):
for middle in self.application.middleware:
middle.process_request(self)
super(MyRequestHandler, self).prepare()
def finish(self):
for middle in self.application.middleware:
middle.process_response(self)
伊谢尔伦2017-04-17 16:50:32
必須是元類,用來控制類別的創建。
範例程式碼,假設登入後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