美文网首页
Tornado #4 支持restful风格API(利用猴子补丁

Tornado #4 支持restful风格API(利用猴子补丁

作者: 平仄_pingze | 来源:发表于2018-06-11 14:28 被阅读114次

    文中所指的restful风格路由指:访问/user/123,可以进入/user路由,并获取id=123。
    Tornado本身不支持,访问/user/123会返回404。

    在项目中创建一个文件 monkeypatch.py:

    
    import re
    from tornado.routing import PathMatches
    from tornado.web import RequestHandler, HTTPError
    from tornado.util import basestring_type
    from tornado.escape import url_unescape, _unicode
    
    VALID_PARAM_TYPE = ['str', 'int', 'float']
    
    def patch_route_restful():
        '''
        猴子补丁,目的是支持 GET /user/123 形式的restful风格路由
        :return:
        '''
    
        def __init__(self, path_pattern):
            if isinstance(path_pattern, basestring_type):
                self.param_type = None
                self.param_name = None
                if not path_pattern.endswith('$'):
                    # MONKEYPATCH 支持<int:uid>形式结尾的匹配
                    param_pattern = re.compile(r'<(.+?):(.+?)>$')
                    if re.search(param_pattern, path_pattern):
                        self.param_type, self.param_name = re.search(param_pattern, path_pattern).groups()
                        if self.param_type not in VALID_PARAM_TYPE:
                            raise SyntaxError('Unsupport param type')
                        path_pattern = re.sub(param_pattern, r'(\w+)$', path_pattern)
                    else:
                        path_pattern += '$'
    
                self.regex = re.compile(path_pattern)
            else:
                self.regex = path_pattern
    
            assert len(self.regex.groupindex) in (0, self.regex.groups), \
                ("groups in url regexes must either be all named or all "
                 "positional: %r" % self.regex.pattern)
    
            self._path, self._group_count = self._find_groups()
    
        def _unquote_or_none(s):
            """None-safe wrapper around url_unescape to handle unmatched optional
            groups correctly.
    
            Note that args are passed as bytes so the handler can decide what
            encoding to use.
            """
            if s is None:
                return s
            return url_unescape(s, encoding=None, plus=False)
    
        def match(self, request):
            match = self.regex.match(request.path)
    
            if match is None:
                return None
            if not self.regex.groups:
                return {}
    
            path_args, path_kwargs = [], {}
    
            # Pass matched groups to the handler.  Since
            # match.groups() includes both named and
            # unnamed groups, we want to use either groups
            # or groupdict but not both.
            if self.regex.groupindex:
                path_kwargs = dict(
                    (str(k), _unquote_or_none(v))
                    for (k, v) in match.groupdict().items())
            else:
                # MONKEYPATCH 处理参数
                if match.groups() and match.groups()[0]:
                    param_val = _unquote_or_none(match.groups()[0])
                    try:
                        param_val = eval(self.param_type + '(' + param_val.decode('utf-8') + ')')
                    except:
                        return None
                    path_kwargs = {self.param_name: param_val}
                else:
                    path_args = [_unquote_or_none(s) for s in match.groups()]
    
            return dict(path_args=path_args, path_kwargs=path_kwargs)
    
        def decode_argument(self, value, name=None):
            """Decodes an argument from the request.
    
            The argument has been percent-decoded and is now a byte string.
            By default, this method decodes the argument as utf-8 and returns
            a unicode string, but this may be overridden in subclasses.
    
            This method is used as a filter for both `get_argument()` and for
            values extracted from the url and passed to `get()`/`post()`/etc.
    
            The name of the argument is provided if known, but may be None
            (e.g. for unnamed groups in the url regex).
            """
            try:
                if type(value) != bytes:
                    return value
                return _unicode(value)
            except UnicodeDecodeError:
                raise HTTPError(400, "Invalid unicode in %s: %r" %
                                (name or "url", value[:40]))
    
        PathMatches.__init__ = __init__
        PathMatches.match = match
        RequestHandler.decode_argument = decode_argument
    

    再在初始化app前使用:

    import tornado.web
    from app.lib.monkeypatch import patch_route_restful
    
    patch_route_restful()
    
    def make_app():
        return tornado.web.Application(routes, db=db)
    
    if __name__ == "__main__":
        app = make_app()
        # more code
    

    这时,路由就支持restful风格了。

    可以定义路由和handler如下:

    import tornado.web
    
    class UserHandler(tornado.web.RequestHandler):
        def get(self, uid):
            if uid:
                # 返回一个指定id的
            else:
                # 返回多个
    
    routes = [(r'/user/<int:uid>', UserHandler)]
    

    相关文章

      网友评论

          本文标题:Tornado #4 支持restful风格API(利用猴子补丁

          本文链接:https://www.haomeiwen.com/subject/ijcheftx.html