Heim  >  Fragen und Antworten  >  Hauptteil

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

PHP中文网PHP中文网2766 Tage vor262

Antworte allen(2)Ich werde antworten

  • 天蓬老师

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

    Antwort
    0
  • 伊谢尔伦

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

    Antwort
    0
  • StornierenAntwort