3.2 輸入
下面幾節(jié)主要講解tornado.web.RequestHandler。
回想一下,利用HTTP協(xié)議向服務(wù)器傳參有幾種途徑?
- 查詢字符串(query string),形如key1=value1&key2=value2;
- 請(qǐng)求體(body)中發(fā)送的數(shù)據(jù),比如表單數(shù)據(jù)、json、xml;
- 提取uri的特定部分,如/blogs/2016/09/0001,可以在服務(wù)器端的路由中用正則表達(dá)式截??;
- 在http報(bào)文的頭(header)中增加自定義字段,如X-XSRFToken=itcast。
我們現(xiàn)在來看下tornado中為我們提供了哪些方法來獲取請(qǐng)求的信息。
1. 獲取查詢字符串參數(shù)
get_query_argument(name, default=_ARG_DEFAULT, strip=True)
從請(qǐng)求的查詢字符串中返回指定參數(shù)name的值,如果出現(xiàn)多個(gè)同名參數(shù),則返回最后一個(gè)的值。
default為設(shè)值未傳name參數(shù)時(shí)返回的默認(rèn)值,如若default也未設(shè)置,則會(huì)拋出tornado.web.MissingArgumentError異常。
strip表示是否過濾掉左右兩邊的空白字符,默認(rèn)為過濾。
get_query_arguments(name, strip=True)
從請(qǐng)求的查詢字符串中返回指定參數(shù)name的值,注意返回的是list列表(即使對(duì)應(yīng)name參數(shù)只有一個(gè)值)。若未找到name參數(shù),則返回空列表[]。
strip同前,不再贅述。
2. 獲取請(qǐng)求體參數(shù)
get_body_argument(name, default=_ARG_DEFAULT, strip=True)
從請(qǐng)求體中返回指定參數(shù)name的值,如果出現(xiàn)多個(gè)同名參數(shù),則返回最后一個(gè)的值。
default與strip同前,不再贅述。
get_body_arguments(name, strip=True)
從請(qǐng)求體中返回指定參數(shù)name的值,注意返回的是list列表(即使對(duì)應(yīng)name參數(shù)只有一個(gè)值)。若未找到name參數(shù),則返回空列表[]。
strip同前,不再贅述。
說明
對(duì)于請(qǐng)求體中的數(shù)據(jù)要求為字符串,且格式為表單編碼格式(與url中的請(qǐng)求字符串格式相同),即key1=value1&key2=value2,HTTP報(bào)文頭Header中的"Content-Type"為application/x-www-form-urlencoded 或 multipart/form-data。對(duì)于請(qǐng)求體數(shù)據(jù)為json或xml的,無法通過這兩個(gè)方法獲取。
3. 前兩類方法的整合
get_argument(name, default=_ARG_DEFAULT, strip=True)
從請(qǐng)求體和查詢字符串中返回指定參數(shù)name的值,如果出現(xiàn)多個(gè)同名參數(shù),則返回最后一個(gè)的值。
default與strip同前,不再贅述。
get_arguments(name, strip=True)
從請(qǐng)求體和查詢字符串中返回指定參數(shù)name的值,注意返回的是list列表(即使對(duì)應(yīng)name參數(shù)只有一個(gè)值)。若未找到name參數(shù),則返回空列表[]。
strip同前,不再贅述。
說明
對(duì)于請(qǐng)求體中數(shù)據(jù)的要求同前。 這兩個(gè)方法最常用。
用代碼來看上述六中方法的使用:
# coding:utf-8
import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
from tornado.options import options, define
from tornado.web import RequestHandler, MissingArgumentError
define("port", default=8000, type=int, help="run server on the given port.")
class IndexHandler(RequestHandler):
def post(self):
query_arg = self.get_query_argument("a")
query_args = self.get_query_arguments("a")
body_arg = self.get_body_argument("a")
body_args = self.get_body_arguments("a", strip=False)
arg = self.get_argument("a")
args = self.get_arguments("a")
default_arg = self.get_argument("b", "itcast")
default_args = self.get_arguments("b")
try:
missing_arg = self.get_argument("c")
except MissingArgumentError as e:
missing_arg = "We catched the MissingArgumentError!"
print e
missing_args = self.get_arguments("c")
rep = "query_arg:%s<br/>" % query_arg
rep += "query_args:%s<br/>" % query_args
rep += "body_arg:%s<br/>" % body_arg
rep += "body_args:%s<br/>" % body_args
rep += "arg:%s<br/>" % arg
rep += "args:%s<br/>" % args
rep += "default_arg:%s<br/>" % default_arg
rep += "default_args:%s<br/>" % default_args
rep += "missing_arg:%s<br/>" % missing_arg
rep += "missing_args:%s<br/>" % missing_args
self.write(rep)
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application([
(r"/", IndexHandler),
])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()

注意:以上方法返回的都是unicode字符串
思考
- 什么時(shí)候設(shè)置default,什么時(shí)候不設(shè)置default?
- default的默認(rèn)值_ARG_DEFAULT是什么?
- 什么時(shí)候使用strip,亦即什么時(shí)候要截?cái)嗫瞻鬃址?,什么時(shí)候不需要?
4. 關(guān)于請(qǐng)求的其他信息
RequestHandler.request 對(duì)象存儲(chǔ)了關(guān)于請(qǐng)求的相關(guān)信息,具體屬性有:
method HTTP的請(qǐng)求方式,如GET或POST;
host 被請(qǐng)求的主機(jī)名;
uri 請(qǐng)求的完整資源標(biāo)示,包括路徑和查詢字符串;
path 請(qǐng)求的路徑部分;
query 請(qǐng)求的查詢字符串部分;
version 使用的HTTP版本;
headers 請(qǐng)求的協(xié)議頭,是類字典型的對(duì)象,支持關(guān)鍵字索引的方式獲取特定協(xié)議頭信息,例如:request.headers["Content-Type"]
body 請(qǐng)求體數(shù)據(jù);
remote_ip 客戶端的IP地址;
-
files 用戶上傳的文件,為字典類型,型如:
{ "form_filename1":[<tornado.httputil.HTTPFile>, <tornado.httputil.HTTPFile>], "form_filename2":[<tornado.httputil.HTTPFile>,], ... }tornado.httputil.HTTPFile是接收到的文件對(duì)象,它有三個(gè)屬性:
- filename 文件的實(shí)際名字,與form_filename1不同,字典中的鍵名代表的是表單對(duì)應(yīng)項(xiàng)的名字;
- body 文件的數(shù)據(jù)實(shí)體;
- content_type 文件的類型。 這三個(gè)對(duì)象屬性可以像字典一樣支持關(guān)鍵字索引,如request.files["form_filename1"][0]["body"]。
我們來實(shí)現(xiàn)一個(gè)上傳文件并保存在服務(wù)器本地的小程序upload.py:
# coding:utf-8
import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
from tornado.options import options, define
from tornado.web import RequestHandler
define("port", default=8000, type=int, help="run server on the given port.")
class IndexHandler(RequestHandler):
def get(self):
self.write("hello itcast.")
class UploadHandler(RequestHandler):
def post(self):
files = self.request.files
img_files = files.get('img')
if img_files:
img_file = img_files[0]["body"]
file = open("./itcast", 'w+')
file.write(img_file)
file.close()
self.write("OK")
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application([
(r"/", IndexHandler),
(r"/upload", UploadHandler),
])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()


5. 正則提取uri
tornado中對(duì)于路由映射也支持正則提取uri,提取出來的參數(shù)會(huì)作為RequestHandler中對(duì)應(yīng)請(qǐng)求方式的成員方法參數(shù)。若在正則表達(dá)式中定義了名字,則參數(shù)按名傳遞;若未定義名字,則參數(shù)按順序傳遞。提取出來的參數(shù)會(huì)作為對(duì)應(yīng)請(qǐng)求方式的成員方法的參數(shù)。
# coding:utf-8
import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
from tornado.options import options, define
from tornado.web import RequestHandler
define("port", default=8000, type=int, help="run server on the given port.")
class IndexHandler(RequestHandler):
def get(self):
self.write("hello itcast.")
class SubjectCityHandler(RequestHandler):
def get(self, subject, city):
self.write(("Subject: %s<br/>City: %s" % (subject, city)))
class SubjectDateHandler(RequestHandler):
def get(self, date, subject):
self.write(("Date: %s<br/>Subject: %s" % (date, subject)))
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application([
(r"/", IndexHandler),
(r"/sub-city/(.+)/([a-z]+)", SubjectCityHandler), # 無名方式
(r"/sub-date/(?P<subject>.+)/(?P<date>\d+)", SubjectDateHandler), # 命名方式
])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()


建議:提取多個(gè)值時(shí)最好用命名方式。