Tornado #4 支持restful风格API(利用猴子补丁
2018-06-11 本文已影响114人
平仄_pingze
文中所指的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)]