Home  >  Q&A  >  body text

装饰模式 - python tornado中如何给每个服务器请求动态加上装饰器?

PHP中文网PHP中文网2766 days ago267

reply all(2)I'll reply

  • 天蓬老师

    天蓬老师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)
            
            
                                                                        

    reply
    0
  • 伊谢尔伦

    伊谢尔伦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

    reply
    0
  • Cancelreply