在Tornado中渲染请求类型
在Tornado框架中,怎么区分不同的请求类型呢?还有,正确的请求分离方法是什么?比如说,当我访问 /item/1.xml 时,我希望得到xml格式的响应,而访问 /item/1.html 时则希望得到一个合适的html页面。
可以像这样实现:
def getXML():
return self.render('somexmlresult.xml')
def getHTML():
return self.rendeR('htmlresult.html')
或者
def get():
if request == 'xml':
return self.render('somexmlresult.xml')
elif request == 'html':
return self.render('htmlresult.html')
~ 编辑 ~ 我想要的实现方式类似于Rails框架中的做法,可以在这里查看。
3 个回答
mimetype(媒体类型)是处理这个问题的正确方式,不过我能理解普通用户希望以更简单的方式获取他们想要的数据格式。
为了保持与符合标准的库等的兼容性,你应该根据请求的mimetype来确定响应的类型,并在响应头中使用合适的mimetype。
为了实现这一点而不破坏任何东西,可以添加一个解析器,检查请求的URI是否有一个后缀,这个后缀与路由可以响应的一组定义后缀相匹配。如果匹配,并且mimetype尚未指定,就将传入的mimetype更改为与后缀相对应的正确类型。
确保最终的决定是基于提供的mimetype,而不是后缀。
这样,其他人就可以以他们习惯的方式与您的RESTful服务进行交互,同时你也能保持对人类友好的使用体验。
~ 编辑 ~
这里有一个正则表达式的例子,用来检查URI是否以 .js | .html | .xml | .json 结尾。这是基于你提供完整的URI。
(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*\.(?:js|html|xml|json))(?:\?([^#]*))?(?:#(.*))?
这里有一个更容易理解但不那么强大的例子
^https?://(?:[a-z\-]+\.)+[a-z]{2,6}(?:/[^/#?]+)+\.(?:js|html|xml|json)$
这些正则表达式来自 rfc2396
我更喜欢那种自我描述的链接,就像RESTful应用程序一样。链接中的某一部分不一定要表示资源的格式。比如说,http://www.enterprise.com/customer/abc/order/123 这个链接应该能表示这个资源,不管它是xml、html还是json格式。要发送请求的格式,可以把它作为请求参数之一来发送。
http://www.enterprise.com/customer/abc/order/123?mimetype=application/xml
http://www.enterprise.com/customer/abc/order/123?mimetype=application/json
http://www.enterprise.com/customer/abc/order/123?mimetype=text/html
使用请求参数来转换成合适的格式。
首先,设置处理程序来处理一种叫做REST风格的URI。这种方式使用了两部分正则表达式,分别用来寻找一个ID和可能的请求格式(比如html、xml、json等)。
class TaskServer(tornado.web.Application):
def __init__(self, newHandlers = [], debug = None):
request_format = "(\.[a-zA-Z]+$)?"
baseHandlers = [
(r"/jobs" + request_format, JobsHandler),
(r"/jobs/", JobsHandler),
(r"/jobs/new" + request_format, NewJobsHandler),
(r"/jobs/([0-9]+)/edit" + request_format, EditJobsHandler)
]
for handler in newHandlers:
baseHandlers.append(handler)
tornado.web.Application.__init__(self, baseHandlers, debug = debug)
接下来,在处理程序中定义一个可以重复使用的函数,叫做parseRestArgs(我把它放在了一个BaseHandler里,但为了方便理解,这里单独贴出来)。这个函数的作用是把ID和请求格式分开。因为你应该期待ID是按照特定的顺序出现的,所以我把它们放在一个列表里。
get函数可以进一步抽象化,但它展示了将你的逻辑拆分成不同请求格式的基本思路……
class JobsHandler(BaseHandler):
def parseRestArgs(self, args):
idList = []
extension = None
if len(args) and not args[0] is None:
for arg in range(len(args)):
match = re.match("[0-9]+", args[arg])
if match:
slave_id = int(match.groups()[0])
match = re.match("(\.[a-zA-Z]+$)", args[-1])
if match:
extension = match.groups()[0][1:]
return idList, extension
def get(self, *args):
### Read
job_id, extension = self.parseRestArgs(args)
if len(job_id):
if extension == None or "html":
#self.render(html) # Show with some ID voodoo
pass
elif extension == 'json':
#self.render(json) # Show with some ID voodoo
pass
else:
raise tornado.web.HTTPError(404) #We don't do that sort of thing here...
else:
if extension == None or "html":
pass
# self.render(html) # Index- No ID given, show an index
elif extension == "json":
pass
# self.render(json) # Index- No ID given, show an index
else:
raise tornado.web.HTTPError(404) #We don't do that sort of thing here...