天蓬老师2017-04-17 16:50:32
tornado provides methods such as prepare and finish in RequestHandler. prepare is called before the framework calls get/post/... these methods. finish is called by the framework when the response is finally written back. So you can use this to implement functions similar to middleware in django. For example, login check, permission verification, and response modification results can all be completed in 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
Must be a metaclass, used to control the creation of classes.
Sample code, assuming that get_user
能返回用户名,即类B
is not logged in after logging in:
# -*- 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()
Output:
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
Then follow this idea and transform it yourself.
Decorator version:
# -*- 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()
Output:
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