以下介绍都是建立在python2.x的基础上面,tornado使用任意版本皆可。
如果我们需要对外提供一个http server(web api)/websocket server时,我们都可以使用tornado,以下是一个基于tornado的rest的应用简介。
当我们下载好了tornado以后,可以按照tornado的文档demo,复制一份监听代码过来,代码如下:
importtornado.ioloopimporttornado.webclassMainHandler(tornado.web.RequestHandler):defget(self):
self.write("Hello, world")application=tornado.web.Application([
(r"/", MainHandler),
])if__name__=="__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
View Code
这一段代码在运行时就已经可以开始监听并返回信息了,然后开始按照rest的规范开始改写,我们首先需要对url的path进行定位,根据path执行相对应的代码,则有了以下代码:
importos.pathimporttornado.escapeimporttornado.ioloopimporttornado.optionsimporttornado.webimporttornado.websocketimporttornado.httpserverimporttoolsimportapplogicfromconfigimportconfigclassApplication(tornado.web.Application):def__init__(self):
handlers=[
(r"/.*?", WebHandler),
]
settings=dict(
cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
template_path=os.path.join(os.path.dirname(__file__),"templates"),
static_path=os.path.join(os.path.dirname(__file__),"static"),
xsrf_cookies=False
)
tornado.web.Application.__init__(self, handlers, **settings)classWebHandler(tornado.web.RequestHandler):defget(self):
path=self.request.path
command= path[path.rfind('/') +1:]
result=applogic.execute(self, command, self.get_argument)ifresult:
to_message=tools.dumps(result)
self.write(to_message)defpost(self):
path=self.request.path
command= path[path.rfind('/') +1:]
obj=tools.loads(self.request.body)
result=applogic.execute(self, command, obj)ifresult:
to_message=tools.dumps(result)
self.write(to_message)defmain():
app=Application()
http_server=tornado.httpserver.HTTPServer(app)
http_server.bind(config.default['main_port'], config.default['host'])
tornado.ioloop.IOLoop.instance().start()
View Code
我们对path做了最后一级目录的截取,由于客观原因,多级目录的则需要看客们自行实现。
applogic是我们的具体的逻辑代码门面类,因为是脚本语言的关系,所以这里使用一个相对巧妙的ioc,代码如下:
defexecute(handler, command, obj):ifmain_map.has_key(command):
result=main_map[command](handler, obj)ifresult:
response.send(handler, result)else:
handler.send_error(404)defcreate(handler, obj):
flag=papercache.push(obj)
result={}ifflag:
result={'code':0
}else:
result={'code':1,'errorMsg':'param is error'}returnresultdefgrab(handler, obj):
id= obj.has_key('id')andobj['id']orNone
result={'code':1,'errorMsg':'redpaper is empty','money':0
}ifid:
money=papercache.pop_queue(id)ifmoney:
result={'money': money,'code': 0
}returnresult
main_map={'create':create,'grab':grab
}
View Code
我们将具体的执行逻辑,放在定义好的function里面,然后将function的引用放在一个字典里面,然后根据command(最后一级目录对应的字符串),来执行具体的代码。
我们将返回的数据做了一个封装,因为实际应用里面的数据格式,可能采用的是json,或者是其它自定义的协议,所以我们有一个response的封装,代码如下:
defsend(handler, obj):iftype(handler)isnotNone:
obj=tools.dumps(obj)
handler.write(obj)
View Code
tools的代码如下:
importjsondefdumps(obj):
obj=toUnicode(obj)ifobj:
obj=json.dumps(obj)
obj=str(obj)returnobjdefloads(obj):ifobj:
obj=json.loads(str(obj))
obj=toUtf8(obj)returnobj
View Code
因为可能存在中文的关系,所以加了一个Utf8的转换,,基于websocket的也是同理,则在监听的时候,使用WebSocketHandle,代码如下:
importos.pathimporttornado.escapeimporttornado.ioloopimporttornado.optionsimporttornado.webimporttornado.websocketimporttornado.httpserverimporttoolsimportapplogicfromconfigimportconfigclassApplication(tornado.web.Application):def__init__(self):
handlers=[
(r"/web", MainHandler)
(r"/.*?", WebHandler),
]
settings=dict(
cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
template_path=os.path.join(os.path.dirname(__file__),"templates"),
static_path=os.path.join(os.path.dirname(__file__),"static"),
xsrf_cookies=False
)
tornado.web.Application.__init__(self, handlers, **settings)classMainHandler(tornado.websocket.WebSocketHandler):defallow_draft76(self):returnTruedefopen(self):
token= self.get_cookie('token')ifnottoken:PRint"long not token"self.close()else:print('connect')defon_close(self):print('close');defon_message(self, message):
obj=tools.loads(message)
command= obj.has_key('command')andobj['command']orNone
body= obj.has_key('body')andobj['body']orNone
result=applogic.execute(self, command, body)ifresult:
to_message=tools.dumps(result)
self.write(to_message)returnclassWebHandler(tornado.web.RequestHandler):defget(self):
path=self.request.path
command= path[path.rfind('/') +1:]
result=applogic.execute(self, command, self.get_argument)ifresult:
to_message=tools.dumps(result)
self.write(to_message)defpost(self):
path=self.request.path
command= path[path.rfind('/') +1:]
obj=tools.loads(self.request.body)
result=applogic.execute(self, command, obj)ifresult:
to_message=tools.dumps(result)
self.write(to_message)defmain():
app=Application()
http_server=tornado.httpserver.HTTPServer(app)
http_server.bind(config.default['main_port'], config.default['host'])
tornado.ioloop.IOLoop.instance().start()
View Code
可能你们会觉得怎么多了个command和body出来了,因为我的通讯协议是假定{"command":"", "body":""},这样的json格式。
那一个简易的基于python的rest服务和websocket通讯服务器到这里就结束了