簡單的web services: html
如今咱們將開始瞭解什麼是tornado,tornado能夠作什麼。咱們來經過分析tornado實現的一個簡單web service 例子開始吧。python
Hello Tornado 程序員
tornado 是一個能夠處理http請求的框架,你的工做是做爲一個程序員,編寫一個handlers來響應一個標準的http請求。,下面是這個例子的全部代碼:web
範例1:hello.py正則表達式
import tornado.httpserver 瀏覽器
import tornado.ioloop app
import tornado.options 框架
import tornado.web curl
from tornado.options import define, options
define(「port」, default=8000, help=」run on the given port」, type=int)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument(‘greeting’, ’Hello’)
self.write(greeting + ’, friendly user!’)
if __name__ == 」__main__「:
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
實際上咱們大部分的工做是使用tornado的接口去定義一個類來擴展tornado 的RequestHandler類,在這個例子中,咱們將會定義一個簡單的應用來監遵從端口得到的請求,而且返回一個響應值。
你能夠經過命令行去試着運行和測試你本身的代碼:
$ python hello.py –port=8000
如今你能夠經過瀏覽器訪問http://localhost:8000/,或者在另一個獨立的命令行窗口使用curl命令測試這個應用:
$ curl http://localhost:8000/
Hello, friendly user!
$ curl http://localhost:8000/?greeting=Salutations
Salutations, friendly user!
讓咱們回到這個例子下,一步一步對它進行分析吧!
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
在程序的最上面,咱們導入各類tornado的函數庫,這裏還包括更多的tornado函數庫,可是在這個例子中你只須要導入四個函數庫就能夠運行了:
from tornado.options import define, options
define(「port」, default=8000, help=」run on the given port」, type=int)
tornado集成了一些有用的函數在tornado.options裏面,在這裏咱們使用這個函數庫來完成監聽HTTP請求的應用,這些參數將會對全部 對象生效,若是你是在命令行中輸入–help,你將會看到全部的幫助信息及其定義。若是你沒有輸入一個值,那麼default變量後面的參數將會做爲默認 值開始執行,假如你輸入的是一個錯誤的類型,程序將會拋出一個錯誤,在這裏容許使用一個整數型數據做爲監聽的端口,若是沒有輸入任何數據,程序將會使用默 認端口8000。
class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument(‘greeting’, ’Hello’)
self.write(greeting + ’, friendly user!’)
這是一個tornado處理請求的類,當出現請求時,tornado實例將會調用這個類和方法。在這個例子中,咱們只定義了一個get方法,這意味着程序只能處理HTTP GET的請求,在後面咱們將會學習到如何使用更多接口去響應HTTP不一樣的請求。
greeting = self.get_argument(‘greeting’, ’Hello’)
tornado的請求類擁有不少有用的方法,好比get_argument,這個方法可讓咱們得到請求字符串中的變量(若是請求中不包含字符串,tornado將會使用第二種方法替代get_argument,並使用默認值)
self.write(greeting + ‘, friendly user!’)
第二個方法是使用一個請求的方法,當請求中有字符串變量時,咱們將會獲取這個字符串,而且插入一個問候語響應這條請求。
if __name__ == 」__main__「:
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
tornado的應用將會從這裏開始執行,首先咱們經過tornado的options函數庫去解析命令,當咱們建立一個tornado應用類的實例的時 候,全部導入的變量將會傳送到類的__init__方法中,這個方法將會告訴tornado須要調用哪個handle ,咱們將會在後面對此作詳細的說明。
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
程序將從這裏開始執行,建立一個實例,咱們經過應用tornado的HTTPServer對象來監聽指定的端口在命令行中獲取的數據,而後咱們建立一個tornado的IOLoop實例,最後這個程序將會開始接收HTTP請求。
操做的變量
讓咱們再回頭看看helly.py這個例子的第一行:
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
這個handlers很是重要,咱們將會在後面對它進行更詳細的解釋,它是一個元組的列表,咱們使用一個正則表達式將它與RequestHandler進行匹配,你能夠添加更多的列表來指定更多須要匹配的RequestHandler。
使用正則表達式來指定URL
tornado使用正則表達式來匹配URL的http請求(RequestHandler的類在URL後面,不包括主機名和查詢字符串的片斷),它們應該 包含起始行和結束行。當一個正則表達式匹配一個HTTP請求時,匹配的內容將會做爲參數傳遞給RequestHandler的類。咱們將會在下一個例子了 解到它是如何工做的。
字符串服務
咱們將經過一個稍微複雜一些的例子1-2去介紹更多關於tornado的設計理念.
例1-2 Handling input: string_service.py
import textwrap
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
define(「port」, default=8000, help=」run on the given port」, type=int)
class ReverseHandler(tornado.web.RequestHandler):
def get(self, input):
self.write(input[::-1])
class WrapHandler(tornado.web.RequestHandler):
def post(self):
text = self.get_argument(‘text’)
width = self.get_argument(‘width’, 40)
self.write(textwrap.fill(text, width))
if __name__ == 」__main__「:
tornado.options.parse_command_line()
app = tornado.web.Application(
handlers=[
(r"/reverse/(\w+)", ReverseHandler),
(r"/wrap", WrapHandler)
]
)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
和第一個例子同樣,你能夠像這樣在命令行中運行它
$ python string_service.py –port=8000
這個程序是一個用於處理字符串的基礎web框架,如今你能夠對它作兩件事情。
第一:經過GET向 /reverse/string請求返回一個特定的字符串,相似這樣的URL結構
$ curl http://localhost:8000/reverse/stressed
desserts
$ curl http://localhost:8000/reverse/slipup
pupils
第二:經過POST向/wrap提交請求,並將返回的字符串顯示出來。這個請求包括一個無長度限制的字符串,在程序中,咱們經過get_argument來接收這個字符串變量。你能夠這麼測試:
$ curl http://localhost:8000/wrap »
-d text=Lorem+ipsum+dolor+sit+amet,+consectetuer+adipiscing+elit.
Lorem ipsum dolor sit amet, consectetuer
adipiscing elit.
curl命令顯示的是兩行命令,可是咱們輸入的應該只是一行的命令,因此咱們使用雙引字符(>>)來表示命令行的延續
這個字符串服務的例子大部分代碼和上一個例子是相同的,讓咱們將注意力集中到那些新增的不一樣代碼中來,首先讓咱們來看看程序中傳送給handlers的變量有什麼不一樣:
app = tornado.web.Application(handlers=[
(r"/reverse/(\w+)", ReverseHandler),
(r"/wrap", WrapHandler)
])
在這個例子的代碼中,咱們能夠看到有兩個RequestHandlers實例化的類對Handlers進行處理,第一個tornado請求的地址按照下面的正則表達式進行匹配:
/reverse/(\w+)
這個正則表達式告訴tornado,他將會匹配一個/reverse/後面跟着一個或多個數字、字母的字符串,括號的含義是告訴tornado,須要將括 號中匹配的字符串做爲RequestHandler的請求參數,咱們能夠查看ReverseHandler的定義去了解它是如何工做的:
class ReverseHandler(tornado.web.RequestHandler):
def get(self, input):
self.write(input[::-1])
你能夠看到這個類中,get方法須要輸入一個額外的參數,這個參數是在第一個正則表達式中,徹底匹配括號內的表達式的一個字符串,(假若有多個括號分割的正則表達式,能夠按照相同的順序在get中添加接收的參數的變量)
如今,讓咱們來看看WrapHandler的定義:
class WrapHandler(tornado.web.RequestHandler):
def post(self):
text = self.get_argument(‘text’)
width = self.get_argument(‘width’, 40)
self.write(textwrap.fill(text, width))
WrapHandler的類會處理從請求中傳過來的字符串。咱們能夠看到這個類只定義了一個post的方法,這意味着它只能接收HTTP post的方法。咱們以前是用過這個RequestHandler對象來抓取從請求中傳過來的字符串做爲變量。如今咱們可使用一樣的方法去抓取POST 請求傳過來的變量(tornado能夠解析POST傳過來的URL字符串或參數),一旦咱們抓取到了字符串及其長度的參數,就可使用python內置的 textwrap函數庫庫去轉換字符串,而且經過HTTP返回生成的字符串做爲響應數據。
更多RequestHandlers
到目前爲止,咱們基於RequestHandler開發這些對象:如何經過HTTP獲取參數(使用get_argument 獲取GET和POST傳遞過來的參數)和如何編寫HTTP相應信息(使用write方法)。在咱們開始後面的章節學習以前,再給你介紹一些應用 RequestHandler須要注意的地方,以及tornado如何去使用它。
http方法:
截止目前討論的都是關於RequestHandler定義單個HTTP方法的問題,其實在一個類中,能夠定義或使用多個 handler,綁定多個功能到一個類中是一種很好的管理方式,你可能會編寫一個處理程序去獲取參數,而且經過POST和GET對象改變數據庫中一個特定 ID的值,例如:用一個GET方法中將抓取請求的ID和參數,而且使用POST方法修改數據庫中對應ID的值。
# matched with (r」/widget/(\d+)」, WidgetHandler)
class WidgetHandler(tornado.web.RequestHandler):
def get(self, widget_id):
widget = retrieve_from_db(widget_id)
self.write(widget.serialize())
def post(self, widget_id):
widget = retrieve_from_db(widget_id)
widget['foo'] = self.get_argument(‘foo’)
save_to_db(widget)
截止目前咱們討論的例子中只使用了GET 和 POST的方法,實際上tornado還支持更多的HTTP方法(GET,POST,PUT,DELETE,HEAD,OPTIONS),在你編寫的 RequestHandler類中能夠經過定義不一樣的方法去使用他們。在下面的虛擬案例中,咱們將會使用HEAD方法去判斷請求中的frob是否存在於數 據庫中,當存在時咱們經過GET方法將frob對應的數據庫信息返回給請求的客戶端:
# matched with (r」/frob/(\d+)」, FrobHandler)
class FrobHandler(tornado.web.RequestHandler):
def head(self, frob_id):
frob = retrieve_from_db(frob_id)
if frob is not None:
self.set_status(200)
else:
self.set_status(404)
def get(self, frob_id):
frob = retrieve_from_db(frob_id)
self.write(frob.serialize())
HTTP狀態碼:
在上一個展現的例子中,你能夠在你的RequestHandler使用set_status()設置HTTP狀態碼到客戶端的相應信息中,下面是一些比較重要的狀態碼。tornado也能夠根據你的相應自動去返回對應的狀態碼,在這裏咱們只列出比較經常使用的幾個狀態碼:
404 Not Found
若是HTTP請求的路徑不存在,tornado將會經過RequestHandler類自動將404(Not Found)返回給客戶端
400 Bad Request
若是使用get_argument沒有獲取到默認的參數哦,或者沒有找到定義的參數名,tornado將會自動將400(Bad Request)返回給客戶端。
405 Method Not Allowed
若是經過HTTP傳進來的請求沒有找到RequestHandler類中對應的方法(例如使用POST請求,可是handler方法對應的類倒是GET方法)tornado將會返回一個405(Method Not Allowed)給客戶端。
500 Internal Server Error
當遇到任何應用服務異常退出的錯誤時,tornado將會返回500( Internal Server Error)給客戶端,代碼中任何意外退出均可能致使tornado返回一個500錯誤代碼。
200 OK
若是請求成功完成了,而且沒有設置其它相應代碼,tornado默認將會自動返回一個200(OK)的響應給客戶端。
當出現一個錯誤出現時,tornado默認將會發送一個包含錯誤代碼和錯誤信息的html頁面給客戶端,若是你想要替換默認響應的錯誤信息成自定義的頁 面,你能夠在你的RequestHandler中重寫write_error方法,例子1-3將會向你展現咱們修改的hello.py,讓你瞭解如何將初 始化的錯誤信息從新改寫。
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
define(「port」, default=8000, help=」run on the given port」, type=int)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument(‘greeting’, ’Hello’)
self.write(greeting + ’, friendly user!’)
def write_error(self, status_code, **kwargs):
self.write(「Gosh darnit, user! You caused a %d error.」 % status_code)
if __name__ == 」__main__「:
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
在當咱們使用POST向handler發送請求時,將會出現下面的響應,由於咱們已經把tornado默認的錯誤響應重寫了:
$ curl -d foo=bar http://localhost:8000/
Gosh darnit, user! You caused a 405 error.
下一步
如今你對tornado已經有了基本的理解,咱們但願你能有迫切學習更多知識的動力。在後續的章節,咱們將向你展現更多的功能和技術,幫助你使用tornado去創建一個完整的web server和web應用。
原創翻譯,首發地址:http://blog.xihuan.de/tech/web/tornado/