根据字符串路由到方法
我想在Python中根据一个字符串来调用方法,这个方式有点像Tornado根据URL来处理请求的方式。举个例子,如果我有一个字符串“/hello”,我想调用某个类里的“hello”方法。我查了一下Python的路由,找到了以下的解决方案:
from routes import Mapper
class Test():
def hello(self):
print "hello world"
map = Mapper()
map.connect(None, '/hello', controller=Test(), action='hello')
match = map.match("/hello")
if match != None:
getattr(match['controller'], match['action'])()
不过,我还想增加一个功能,就是能把字符串的一部分当作参数传给这个方法。比如说,对于字符串“hello/5”,我想调用hello(5)
(这和Tornado的路由方式非常相似)。这个功能应该能支持任意数量的参数。有没有人有好的办法来实现这个?能不能用Tornado的路由功能来处理一个不是请求URL的字符串呢?
2 个回答
1
想法相似,但方式不同。这里是代码:
class Xroute(tornado.web.RequestHandler):
def get(self,path):
Router().get(path,self)
def post(self,path):
Router().post(path,self)
class Router(object):
mapper={}
@classmethod
def route(cls,**deco):
def foo(func):
url = deco.get('url') or '/'
if url not in cls.mapper:
method = deco.get('method') or 'GET'
mapper_node = {}
mapper_node['method'] = method
mapper_node['call'] = func
cls.mapper[url] = mapper_node
return func
return foo
def get(self,path,reqhandler):
self.emit(path,reqhandler,"GET")
def post(self,path,reqhandler):
self.emit(path,reqhandler,"POST")
def emit(self,path,reqhandler,method_flag):
mapper = Router.mapper
for urlExp in mapper:
m = re.match('^'+urlExp+'$',path)
if m:
params = (reqhandler,)
for items in m.groups():
params+=(items,)
mapper_node = mapper.get(urlExp)
method = mapper_node.get('method')
if method_flag not in method:
raise tornado.web.HTTPError(405)
call = mapper_node.get('call')
try:
call(*params)
break
except Exception,e:
print e
raise tornado.web.HTTPError(500)
else:
raise tornado.web.HTTPError(404)
@Router.route(url=r"hello/([a-z]+)",method="GET")
def test(req, who):
#http://localhost:8888/hello/billy
req.write("Hi," + who)
@Router.route(url=r"greetings/([a-z]+)",method="GET|POST")
def test2(req, who):
#http://localhost:8888/greetings/rowland
test(req, who)
@Router.route(url=r"book/([a-z]+)/(\d+)",method="GET|POST")
def test3(req,categories,bookid):
#http://localhost:8888/book/medicine/49875
req.write("You are looking for a " + categories + " book\n"+"book No. " + bookid)
@Router.route(url=r"person/(\d+)",method="GET|POST")
def test4(req, personid):
#http://localhost:8888/person/29772
who = req.get_argument("name","default")
age = req.get_argument("age",0)
person = {}
person['id'] = personid
person['who'] = who
person['age'] = int(age)
req.write(person)
if __name__=="__main__":
port = 8888
application = tornado.web.Application([(r"^/([^\.|]*)(?!\.\w+)$", Xroute),])
application.listen(port)
tornado.ioloop.IOLoop.instance().start()
1
首先,了解一下你想要做这个“本地”路由系统的原因、用途和最初的任务会很有帮助。
根据你的例子,一个可行的方法就是自己动手做一些东西。
这里有一个非常基础的例子:
class Test():
def hello(self, *args):
print args
urls = ['hello/', 'hello/5', 'hello/5/world/so/good']
handler = Test()
for url in urls:
parts = url.split('/')
method = parts[0]
if hasattr(handler, method):
getattr(handler, method)(*parts[1:])
输出结果是:
('',)
('5',)
('5', 'world', 'so', 'good')
你还可以使用正则表达式来保存网址中的某些部分,分成命名组或位置组。比如,看看django和python-routes是怎么做的。
希望这些对你有帮助。