t3 深入Tornado

?

3.1 Application

settings

前面的學習中,在創建tornado.web.Application的對象時,傳入了第一個參數——路由映射列表。實際上Application類的構造函數還接收很多關于tornado web應用的配置參數。

參數:

debug,設置tornado是否工作在調試模式,默認為False即工作在生產模式。當設置debug=True 后,tornado會工作在調試/開發模式,在此種模式下,tornado為方便開發而提供了幾種特性:

  • 自動重啟,tornado應用會監控的源代碼文件,當有改動保存后便會重啟程序,這可以減少手動重啟程序的次數。需要注意的是,一旦保存的更改有錯誤,自動重啟會導致程序報錯而退出,從而需要保存修正錯誤后手動啟動程序。這一特性也可單獨通過autoreload=True設置;
  • 取消緩存編譯的模板,可以單獨通過compiled_template_cache=False來設置;
  • 取消緩存靜態文件hash值,可以單獨通過static_hash_cache=False來設置;
  • 提供追蹤信息,當RequestHandler或者其子類拋出一個異常而未被捕獲后,會生成一個包含追蹤信息的頁面,可以單獨通過serve_traceback=True來設置。

使用debug參數的方法:

import tornado.web
app = tornado.web.Application([ ], debug=True)  

路由映射

先前在構建路由映射列表的時候,使用的是二元元組,如:

?[(r"/", IndexHandler),]?

對于這個映射列表中的路由,實際上還可以傳入多個信息,如:

[(r"/", Indexhandler),(r"/cpp", lewenHandler, {"subject":"c++"}),url(r"/python", lewenHandler, {"subject":"python"}, name="python_url")
]

?

對于路由中的字典,會傳入到對應的RequestHandler的initialize()方法中:

from tornado.web import RequestHandler
class lewenHandler(RequestHandler):def initialize(self, subject):self.subject = subjectdef get(self):self.write(self.subject)

?

對于路由中的name字段,注意此時不能再使用元組,而應使用tornado.web.url來構建。name是給該路由起一個名字,可以通過調用RequestHandler.reverse_url(name)來獲取該名子對應的url。

# -*- coding: utf-8 -*-
# __auther__ = 'lewen'import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
from tornado.options import options, define
from tornado.web import url, RequestHandlerdefine("port", default=8000, type=int, help="run server on the given port ")class IndexHandler(RequestHandler):def get(self, *args, **kwargs):python_url = self.reverse_url("python_url")self.write('<a href="%s">Tornado </a>' % python_url)class MyHandler(RequestHandler):def initialize(self, subject):self.subject = subjectdef get(self, *args, **kwargs):self.write(self.subject)if __name__ == "__main__":tornado.options.parse_command_line()app = tornado.web.Application([(r"/", IndexHandler),(r"/cpp", MyHandler, {"subject": "c++"}),url(r"/python", MyHandler, {"subject": "python"}, name="python_url")],debug=True)http_server = tornado.httpserver.HTTPServer(app)http_server.listen(options.port)tornado.ioloop.IOLoop.current().start()

?


?



3.2 輸入

下面幾節主要講解tornado.web.RequestHandler。

回想一下,利用HTTP協議向服務器傳參有幾種途徑?

  • 查詢字符串(query string),形如key1=value1&key2=value2;
  • 請求體(body)中發送的數據,比如表單數據、json、xml;
  • 提取uri的特定部分,如/blogs/2016/09/0001,可以在服務器端的路由中用正則表達式截取;
  • 在http報文的頭(header)中增加自定義字段,如X-XSRFToken=lewen。

現在來看下tornado中為提供了哪些方法來獲取請求的信息。

1. 獲取查詢字符串參數

get_query_argument(name, default=_ARG_DEFAULT, strip=True)

從請求的查詢字符串中返回指定參數name的值,如果出現多個同名參數,則返回最后一個的值。

default為設值未傳name參數時返回的默認值,如若default也未設置,則會拋出tornado.web.MissingArgumentError異常。

strip表示是否過濾掉左右兩邊的空白字符,默認為過濾。

get_query_arguments(name, strip=True)

從請求的查詢字符串中返回指定參數name的值,注意返回的是list列表(即使對應name參數只有一個值)。若未找到name參數,則返回空列表 [ ]。

strip同前,不再贅述。

2. 獲取請求體參數

get_body_argument(name, default=_ARG_DEFAULT, strip=True)

從請求體中返回指定參數name的值,如果出現多個同名參數,則返回最后一個的值。

default與strip同前,不再贅述。

get_body_arguments(name, strip=True)

從請求體中返回指定參數name的值,注意返回的是list列表(即使對應name參數只有一個值)。若未找到name參數,則返回空列表[]。

strip同前,不再贅述。

說明

對于請求體中的數據要求為字符串,且格式為表單編碼格式(與url中的請求字符串格式相同),即key1=value1&key2=value2,HTTP報文頭Header中的"Content-Type"為application/x-www-form-urlencoded 或 multipart/form-data。對于請求體數據為json或xml的,無法通過這兩個方法獲取。

3. 前兩類方法的整合

get_argument(name, default=_ARG_DEFAULT, strip=True)

從請求體和查詢字符串中返回指定參數name的值,如果出現多個同名參數,則返回最后一個的值。

default與strip同前,不再贅述。

get_arguments(name, strip=True)

從請求體和查詢字符串中返回指定參數name的值,注意返回的是list列表(即使對應name參數只有一個值)。若未找到name參數,則返回空列表[]。

strip同前,不再贅述。

說明

對于請求體中數據的要求同前。?這兩個方法最常用。

用代碼來看上述六中方法的使用:

# -*- coding: utf-8 -*-
# __auther__ = 'lewen'import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
from tornado.options import options, define
from tornado.web import RequestHandler, MissingArgumentErrordefine("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)# 參數獲取范圍是請全體和查詢url中arg = self.get_argument("a")args = self.get_arguments("a")default_arg = self.get_argument("b", "tornado test")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_argrep += "query_args:%s<br/>" % query_argsrep += "body_arg:%s<br/>" % body_argrep += "body_args:%s<br/>" % body_argsrep += "arg:%s<br/>" % argrep += "args:%s<br/>" % argsrep += "default_arg:%s<br/>" % default_argrep += "default_args:%s<br/>" % default_argsrep += "missing_arg:%s<br/>" % missing_argrep += "missing_args:%s<br/>" % missing_argsself.write(rep)if __name__ == "__main__":tornado.options.parse_command_line()app = tornado.web.Application([(r"/", IndexHandler),],debug=True)http_server = tornado.httpserver.HTTPServer(app)http_server.listen(options.port)tornado.ioloop.IOLoop.current().start()

?

演示結果

注意:以上方法返回的都是unicode字符串

使用時注意思考

  1. 什么時候設置default,什么時候不設置default?
  2. default的默認值_ARG_DEFAULT是什么?
  3. 什么時候使用strip,亦即什么時候要截斷空白字符,什么時候不需要?

4. 關于請求的其他信息

RequestHandler.request 對象存儲了關于請求的相關信息,具體屬性有:

  • method? ? ?HTTP的請求方式,如GET或POST;
  • host? ? ? ? ? 被請求的主機名;
  • uri? ? ? ? ? ? 請求的完整資源標示,包括路徑和查詢字符串;
  • path? ? ? ? ?請求的路徑部分;
  • query? ? ? ?請求的查詢字符串部分;
  • version? ? 使用的HTTP版本;
  • headers? 請求的協議頭,是類字典型的對象,支持關鍵字索引的方式獲取特定協議頭信息,例如:request.headers["Content-Type"]
  • body? ? ? ?請求體數據;
  • remote_ip? 客戶端的IP地址;
  • files? ? ? ? ? ? 用戶上傳的文件,為字典類型,型如:
    {"form_filename1":[<tornado.httputil.HTTPFile>, <tornado.httputil.HTTPFile>],        # 表單名稱:文件對象"form_filename2":[<tornado.httputil.HTTPFile>,],... 
    }

    ?

    tornado.httputil.HTTPFile是接收到的文件對象,它有三個屬性:
    • filename 文件的實際名字,與form_filename1不同,字典中的鍵名代表的是表單對應項的名字;
    • body? ? ? ?文件的數據實體;
    • content_type? ? 文件的類型。 這三個對象屬性可以像字典一樣支持關鍵字索引,如 request.files["form_filename1"][0]["body"]。

實現一個上傳文件并保存在服務器本地的小程序upload.py:

# -*- coding: utf-8 -*-
# __auther__ = 'lewen'import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
from tornado.options import options, define
from tornado.web import RequestHandlerdefine("port", default=8000, type=int, help="run server on the given port.")class IndexHandler(RequestHandler):def get(self):self.write("hello tornado.")class UploadHandler(RequestHandler):def post(self):files = self.request.files  # 用戶上傳的文件,為字典類型img_files = files.get('img')  # 獲取文件對象列表if img_files:img_file = img_files[0]["body"]  # 獲取文件數據with open("./tornado.jpg", 'wb') as file:file.write(img_file)self.write("OK")if __name__ == "__main__":tornado.options.parse_command_line()app = tornado.web.Application([(r"/", IndexHandler),(r"/upload", UploadHandler),], debug=True)http_server = tornado.httpserver.HTTPServer(app)http_server.listen(options.port)tornado.ioloop.IOLoop.current().start()

?

?上傳圖片前端

5. 正則提取uri

tornado中對于路由映射也支持正則提取uri,提取出來的參數會作為RequestHandler中對應請求方式的成員方法參數。若在正則表達式中定義了名字,則參數按名傳遞;若未定義名字,則參數按順序傳遞。提取出來的參數會作為對應請求方式的成員方法的參數。

?

# -*- coding: utf-8 -*-
# __auther__ = 'lewen'import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
from tornado.options import options, define
from tornado.web import RequestHandlerdefine("port", default=8000, type=int, help="run server on the given port.")class IndexHandler(RequestHandler):def get(self):self.write("hello tornado.")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()

?

建議:提取多個值時最好用命名方式。

?


?

?

3.3 輸出

1. write(chunk)

將chunk數據寫到輸出緩沖區。如在之前的示例代碼中寫的:

class IndexHandler(RequestHandler):def get(self):self.write("hello lewen!") 

想一想,可不可以在同一個處理方法中多次使用write方法?

下面的代碼會出現什么效果?

class IndexHandler(RequestHandler):def get(self):self.write("hello lewen 1!")self.write("hello lewen 2!")self.write("hello lewen 3!")

  write方法是寫到緩沖區的,可以像寫文件一樣多次使用write方法不斷追加響應內容,最終所有寫到緩沖區的內容一起作為本次請求的響應輸出。

想一想,如何利用write方法寫json數據?

import jsonclass IndexHandler(RequestHandler):def get(self):stu = {"name":"zhangsan","age":24,"gender":1,}stu_json = json.dumps(stu)self.write(stu_json)

  實際上,可以不用自己手動去做json序列化,當write方法檢測到傳入的chunk參數是字典類型后,會自動幫轉換為json字符串。

?

class IndexHandler(RequestHandler):def get(self):stu = {"name":"zhangsan","age":24,"gender":1,}self.write(stu)

兩種方式有什么差異?

對比一下兩種方式的響應頭header中Content-Type字段,自己手動序列化時為Content-Type:text/html; charset=UTF-8,而采用write方法時為Content-Type:application/json; charset=UTF-8

write方法除了幫將字典轉換為json字符串之外,還幫將Content-Type設置為application/json; charset=UTF-8

2. set_header(name, value)

利用set_header(name, value)方法,可以手動設置一個名為name、值為value的響應頭header字段。

用set_header方法來完成上面write所做的工作。

import jsonclass IndexHandler(RequestHandler):def get(self):stu = {"name":"zhangsan","age":24,"gender":1,}stu_json = json.dumps(stu)self.write(stu_json)self.set_header("Content-Type", "application/json; charset=UTF-8")

?

3. set_default_headers()

該方法會在進入HTTP處理方法前先被調用,可以重寫此方法來預先設置默認的headers。注意:在HTTP處理方法中使用set_header()方法會覆蓋掉在set_default_headers()方法中設置的同名header。

class IndexHandler(RequestHandler):def set_default_headers(self):print "執行了set_default_headers()"# 設置get與post方式的默認響應體格式為jsonself.set_header("Content-Type", "application/json; charset=UTF-8")# 設置一個名為lewen、值為python的headerself.set_header("lewen", "python")def get(self):print "執行了get()"stu = {"name":"zhangsan","age":24,"gender":1,}stu_json = json.dumps(stu)self.write(stu_json)self.set_header("lewen", "i love python") # 注意此處重寫了header中的lewen字段def post(self):print "執行了post()"stu = {"name":"zhangsan","age":24,"gender":1,}stu_json = json.dumps(stu)self.write(stu_json)

... ...

?

終端中打印出的執行順序:

?

get請求方式的響應header:

post請求方式的響應header:

?

4. set_status(status_code, reason=None)

為響應設置狀態碼。

參數說明:

  • status_code? ? ? ? ? ?int類型,狀態碼,若reason為None,則狀態碼必須為下表中的。
  • reason? ? ? ? ? ? ? ? ? ?string類型,描述狀態碼的詞組,若為None,則會被自動填充為下表中的內容。
CodeEnum NameDetails
100CONTINUEHTTP/1.1 RFC 7231, Section 6.2.1
101SWITCHING_PROTOCOLSHTTP/1.1 RFC 7231, Section 6.2.2
102PROCESSINGWebDAV RFC 2518, Section 10.1
200OKHTTP/1.1 RFC 7231, Section 6.3.1
201CREATEDHTTP/1.1 RFC 7231, Section 6.3.2
202ACCEPTEDHTTP/1.1 RFC 7231, Section 6.3.3
203NON_AUTHORITATIVE_INFORMATIONHTTP/1.1 RFC 7231, Section 6.3.4
204NO_CONTENTHTTP/1.1 RFC 7231, Section 6.3.5
205RESET_CONTENTHTTP/1.1 RFC 7231, Section 6.3.6
206PARTIAL_CONTENTHTTP/1.1 RFC 7233, Section 4.1
207MULTI_STATUSWebDAV RFC 4918, Section 11.1
208ALREADY_REPORTEDWebDAV Binding Extensions RFC 5842, Section 7.1 (Experimental)
226IM_USEDDelta Encoding in HTTP RFC 3229, Section 10.4.1
300MULTIPLE_CHOICESHTTP/1.1 RFC 7231, Section 6.4.1
301MOVED_PERMANENTLYHTTP/1.1 RFC 7231, Section 6.4.2
302FOUNDHTTP/1.1 RFC 7231, Section 6.4.3
303SEE_OTHERHTTP/1.1 RFC 7231, Section 6.4.4
304NOT_MODIFIEDHTTP/1.1 RFC 7232, Section 4.1
305USE_PROXYHTTP/1.1 RFC 7231, Section 6.4.5
307TEMPORARY_REDIRECTHTTP/1.1 RFC 7231, Section 6.4.7
308PERMANENT_REDIRECTPermanent Redirect RFC 7238, Section 3 (Experimental)
400BAD_REQUESTHTTP/1.1 RFC 7231, Section 6.5.1
401UNAUTHORIZEDHTTP/1.1 Authentication RFC 7235, Section 3.1
402PAYMENT_REQUIREDHTTP/1.1 RFC 7231, Section 6.5.2
403FORBIDDENHTTP/1.1 RFC 7231, Section 6.5.3
404NOT_FOUNDHTTP/1.1 RFC 7231, Section 6.5.4
405METHOD_NOT_ALLOWEDHTTP/1.1 RFC 7231, Section 6.5.5
406NOT_ACCEPTABLEHTTP/1.1 RFC 7231, Section 6.5.6
407PROXY_AUTHENTICATION_REQUIREDHTTP/1.1 Authentication RFC 7235, Section 3.2
408REQUEST_TIMEOUTHTTP/1.1 RFC 7231, Section 6.5.7
409CONFLICTHTTP/1.1 RFC 7231, Section 6.5.8
410GONEHTTP/1.1 RFC 7231, Section 6.5.9
411LENGTH_REQUIREDHTTP/1.1 RFC 7231, Section 6.5.10
412PRECONDITION_FAILEDHTTP/1.1 RFC 7232, Section 4.2
413REQUEST_ENTITY_TOO_LARGEHTTP/1.1 RFC 7231, Section 6.5.11
414REQUEST_URI_TOO_LONGHTTP/1.1 RFC 7231, Section 6.5.12
415UNSUPPORTED_MEDIA_TYPEHTTP/1.1 RFC 7231, Section 6.5.13
416REQUEST_RANGE_NOT_SATISFIABLEHTTP/1.1 Range Requests RFC 7233, Section 4.4
417EXPECTATION_FAILEDHTTP/1.1 RFC 7231, Section 6.5.14
422UNPROCESSABLE_ENTITYWebDAV RFC 4918, Section 11.2
423LOCKEDWebDAV RFC 4918, Section 11.3
424FAILED_DEPENDENCYWebDAV RFC 4918, Section 11.4
426UPGRADE_REQUIREDHTTP/1.1 RFC 7231, Section 6.5.15
428PRECONDITION_REQUIREDAdditional HTTP Status Codes RFC 6585
429TOO_MANY_REQUESTSAdditional HTTP Status Codes RFC 6585
431REQUEST_HEADER_FIELDS_TOO_LARGE AdditionalHTTP Status Codes RFC 6585
500INTERNAL_SERVER_ERRORHTTP/1.1 RFC 7231, Section 6.6.1
501NOT_IMPLEMENTEDHTTP/1.1 RFC 7231, Section 6.6.2
502BAD_GATEWAYHTTP/1.1 RFC 7231, Section 6.6.3
503SERVICE_UNAVAILABLEHTTP/1.1 RFC 7231, Section 6.6.4
504GATEWAY_TIMEOUTHTTP/1.1 RFC 7231, Section 6.6.5
505HTTP_VERSION_NOT_SUPPORTEDHTTP/1.1 RFC 7231, Section 6.6.6
506VARIANT_ALSO_NEGOTIATESTransparent Content Negotiation in HTTP RFC 2295, Section 8.1 (Experimental)
507INSUFFICIENT_STORAGEWebDAV RFC 4918, Section 11.5
508LOOP_DETECTEDWebDAV Binding Extensions RFC 5842, Section 7.2 (Experimental)
510NOT_EXTENDEDAn HTTP Extension Framework RFC 2774, Section 7 (Experimental)
511NETWORK_AUTHENTICATION_REQUIREDAdditional HTTP Status Codes RFC 6585, Section 6
-
class Err404Handler(RequestHandler):"""對應/err/404"""def get(self):self.write("hello lewen")self.set_status(404) # 標準狀態碼,不用設置reasonclass Err210Handler(RequestHandler):"""對應/err/210"""def get(self):self.write("hello lewen")self.set_status(210, "lewen error") # 非標準狀態碼,設置了reasonclass Err211Handler(RequestHandler):"""對應/err/211"""def get(self):self.write("hello lewen")self.set_status(211) # 非標準狀態碼,未設置reason,Unknow

?

?

5. redirect(url)

告知瀏覽器跳轉到url。

class IndexHandler(RequestHandler):"""對應/"""def get(self):self.write("主頁")class LoginHandler(RequestHandler):"""對應/login"""def get(self):self.write('<form method="post"><input type="submit" value="登陸"></form>')def post(self):self.redirect("/")

?

6. send_error(status_code=500, **kwargs)

拋出HTTP錯誤狀態碼 status_code,默認為500,kwargs為可變命名參數。使用send_error拋出錯誤后tornado會調用write_error()方法進行處理,并返回給瀏覽器處理后的錯誤頁面。

class IndexHandler(RequestHandler):def get(self):self.write("主頁")self.send_error(404, content="出現404錯誤")

?

注意:默認的write_error()方法不會處理send_error拋出的kwargs參數,即上面的代碼中content="出現404錯誤"是沒有意義的。

嘗試下面的代碼會出現什么問題?

class IndexHandler(RequestHandler):def get(self):self.write("主頁")self.send_error(404, content="出現404錯誤")self.write("結束") # 在send_error再次向輸出緩沖區寫內容?

注意:使用send_error()方法后就不要再向輸出緩沖區寫內容了!

7. write_error(status_code, **kwargs)

用來處理send_error拋出的錯誤信息并返回給瀏覽器錯誤信息頁面。可以重寫此方法來定制自己的錯誤顯示頁面。

class IndexHandler(RequestHandler):def get(self):err_code = self.get_argument("code", None) # 注意返回的是unicode字符串,下同err_title = self.get_argument("title", "")err_content = self.get_argument("content", "")if err_code:self.send_error(err_code, title=err_title, content=err_content)else:self.write("主頁")def write_error(self, status_code, **kwargs):self.write(u"<h1>出錯了,程序員GG正在趕過來!</h1>")self.write(u"<p>錯誤名:%s</p>" % kwargs["title"])self.write(u"<p>錯誤詳情:%s</p>" % kwargs["content"])?

展示錯誤信息

?


?

?

3.4 接口與調用順序

下面的接口方法是由tornado框架進行調用的,可以選擇性的重寫這些方法。

1. initialize()

對應每個請求的處理類Handler在構造一個實例后首先執行initialize()方法。在講輸入時提到,路由映射中的第三個字典型參數會作為該方法的命名參數傳遞,如:

class ProfileHandler(RequestHandler):def initialize(self, database):self.database = databasedef get(self):...app = Application([(r'/user/(.*)', ProfileHandler, dict(database=database)),])

?

此方法通常用來初始化參數(對象屬性),很少使用。

2. prepare()

預處理,即在執行對應請求方式的HTTP方法(如get、post等)前先執行,注意:不論以何種HTTP方式請求,都會執行prepare()方法。

以預處理請求體中的json數據為例:

import jsonclass IndexHandler(RequestHandler):def prepare(self):if self.request.headers.get("Content-Type").startswith("application/json"):self.json_dict = json.loads(self.request.body)else:self.json_dict = Nonedef post(self):if self.json_dict:for key, value in self.json_dict.items():self.write("<h3>%s</h3><p>%s</p>" % (key, value))def put(self):if self.json_dict:for key, value in self.json_dict.items():self.write("<h3>%s</h3><p>%s</p>" % (key, value))

用post方式發送json數據時:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?用put方式發送json數據時:

? ? ? ? ? ? ? ? ? ? ??

3. HTTP方法

方法描述
get請求指定的頁面信息,并返回實體主體。
head類似于get請求,只不過返回的響應中沒有具體的內容,用于獲取報頭
post向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會導致新的資源的建立和/或已有資源的修改。
delete請求服務器刪除指定的內容。
patch請求修改局部數據。
put從客戶端向服務器傳送的數據取代指定的文檔的內容。
options返回給定URL支持的所有HTTP方法。

4. on_finish()

在請求處理結束后調用,即在調用HTTP方法后調用。通常該方法用來進行資源清理釋放或處理日志等。注意:請盡量不要在此方法中進行響應輸出。

5. set_default_headers()

6. write_error()

7. 調用順序

通過一段程序來看上面這些接口的調用順序。

# -*- coding: utf-8 -*-
# __auther__ = 'lewen'
import json
import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
from tornado.options import options, define
from tornado.web import RequestHandlerdefine("port", default=8000, type=int, help="run server on the given port.")class IndexHandler(RequestHandler):def set_default_headers(self):print("調用了set_default_headers()")def initialize(self):print("調用了initialize()")def prepare(self):print("調用了prepare()")def write_error(self, status_code, **kwargs):print("調用了write_error()")def get(self):print("調用了get()")def post(self):print("調用了post()")self.send_error(200)  # 注意此出拋出了錯誤def on_finish(self):print("調用了on_finish()")if __name__ == "__main__":tornado.options.parse_command_line()app = tornado.web.Application([(r"/", IndexHandler),], debug=True)http_server = tornado.httpserver.HTTPServer(app)http_server.listen(options.port)tornado.ioloop.IOLoop.current().start()

?

在正常情況未拋出錯誤時,調用順序為:

  1. set_defautl_headers()
  2. initialize()
  3. prepare()
  4. HTTP方法
  5. on_finish()

在有錯誤拋出時,調用順序為:

  1. set_default_headers()
  2. initialize()
  3. prepare()
  4. HTTP方法
  5. set_default_headers()
  6. write_error()
  7. on_finish()

?

3.5 練習

?

1.將Application的設置參數(目前只學習了debug)抽離為一個字典類型變量settings,并在構造Application對象時使用settings。
2.熟練使用RequestHandler的各種輸入輸出方法。
3.嘗試抽象出BaseHandler基類,繼承自RequestHandler,并在此基類中實現prepare(解析json數據)、write_error兩個接口。

?












轉載于:https://www.cnblogs.com/wenyule/articles/10353578.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/388334.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/388334.shtml
英文地址,請注明出處:http://en.pswp.cn/news/388334.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vml編輯器

<HTML xmlns:v> <HEAD> <META http-equiv"Content-Type" content"text/html; Charsetgb2312"> <META name"GENERATOR" content"網絡程序員伴侶(Lshdic)2004"> <META name"GENERATORDOWNLOADADDRESS&q…

對數據倉庫進行數據建模_確定是否可以對您的數據進行建模

對數據倉庫進行數據建模Some data sets are just not meant to have the geospatial representation that can be clustered. There is great variance in your features, and theoretically great features as well. But, it doesn’t mean is statistically separable.某些數…

15 并發編程-(IO模型)

一、IO模型介紹 1、阻塞與非阻塞指的是程序的兩種運行狀態 阻塞&#xff1a;遇到IO就發生阻塞&#xff0c;程序一旦遇到阻塞操作就會停在原地&#xff0c;并且立刻釋放CPU資源 非阻塞&#xff08;就緒態或運行態&#xff09;&#xff1a;沒有遇到IO操作&#xff0c;或者通過某種…

arduino消息服務器,在C(Arduino IDE)中將API鏈接消息解析為服務器(示例代碼)

我正在使用Arduino IDE來編程我的微控制器&#xff0c;它有一個內置的Wi-Fi芯片(ESP8266 NodeMCU)&#xff0c;它連接到我的互聯網路由器&#xff0c;然后有一個特定的IP(就像192.168.1.5)。所以我想通過添加到鏈接的消息發送命令(和數據)&#xff0c;然后鏈接變為&#xff1a;…

不提拔你,就是因為你只想把工作做好

2019獨角獸企業重金招聘Python工程師標準>>> 我有個朋友&#xff0c;他30出頭&#xff0c;在500強公司做技術經理。他戴無邊眼鏡&#xff0c;穿一身土黃色的夾克&#xff0c;下面是一條常年不洗的牛仔褲加休閑皮鞋&#xff0c;典型技術高手范。 三 年前&#xff0c;…

python內置函數多少個_每個數據科學家都應該知道的10個Python內置函數

python內置函數多少個Python is the number one choice of programming language for many data scientists and analysts. One of the reasons of this choice is that python is relatively easier to learn and use. More importantly, there is a wide variety of third pa…

C#使用TCP/IP與ModBus進行通訊

C#使用TCP/IP與ModBus進行通訊1. ModBus的 Client/Server模型 2. 數據包格式及MBAP header (MODBUS Application Protocol header) 3. 大小端轉換 4. 事務標識和緩沖清理 5. 示例代碼 0. MODBUS MESSAGING ON TCP/IP IMPLEMENTATION GUIDE 下載地址&#xff1a;http://www.modb…

Hadoop HDFS常用命令

1、查看hdfs文件目錄 hadoop fs -ls / 2、上傳文件 hadoop fs -put 文件路徑 目標路徑 在瀏覽器查看:namenodeIP:50070 3、下載文件 hadoop fs -get 文件路徑 保存路徑 4、設置副本數量 -setrep 轉載于:https://www.cnblogs.com/chaofan-/p/9742633.html

SAP UI 搜索分頁技術

搜索分頁技術往往和另一個術語Lazy Loading&#xff08;懶加載&#xff09;聯系起來。今天由Jerry首先介紹S/4HANA&#xff0c;CRM Fiori和S4CRM應用里的UI搜索分頁的實現原理。后半部分由SAP成都研究院菜園子小哥王聰向您介紹Twitter的懶加載實現。 關于王聰的背景介紹&#x…

萬彩錄屏服務器不穩定,萬彩錄屏 云服務器

萬彩錄屏 云服務器 內容精選換一換內網域名是指僅在VPC內生效的虛擬域名&#xff0c;無需購買和注冊&#xff0c;無需備案。云解析服務提供的內網域名功能&#xff0c;可以讓您在VPC中擁有權威DNS&#xff0c;且不會將您的DNS記錄暴露給互聯網&#xff0c;解析性能更高&#xf…

針對數據科學家和數據工程師的4條SQL技巧

SQL has become a common skill requirement across industries and job profiles over the last decade.在過去的十年中&#xff0c;SQL已成為跨行業和職位描述的通用技能要求。 Companies like Amazon and Google will often demand that their data analysts, data scienti…

C# 讀取CAD文件縮略圖(DWG文件)

//C# 讀取CAD文件縮略圖&#xff08;DWG文件&#xff09; 楊航收集技術資料&#xff0c;分享給大家 //2010-09-04 16:34:58| 分類&#xff1a; C# |字號 訂閱//在不使用任務插件的情況下讀取DWG文件的縮略圖&#xff0c;以便在沒有安裝AutoCAD的計算機上瀏覽。using System;u…

全排列算法實現

版權聲明&#xff1a;本文為博主原創文章&#xff0c;未經博主允許不得轉載。 https://blog.csdn.net/summerxiachen/article/details/605796231.全排列的定義和公式&#xff1a; 從n個數中選取m&#xff08;m<n&#xff09;個數按照一定的順序進行排成一個列&#xff0c;叫…

14.并發容器之ConcurrentHashMap(JDK 1.8版本)

1.ConcurrentHashmap簡介 在使用HashMap時在多線程情況下擴容會出現CPU接近100%的情況&#xff0c;因為hashmap并不是線程安全的&#xff0c;通常我們可以使用在java體系中古老的hashtable類&#xff0c;該類基本上所有的方法都采用synchronized進行線程安全的控制&#xff0c;…

modbus注意幾點

1、 在利用Modbus通訊的過程中&#xff0c;遇到這樣一個問題&#xff0c;即浮點數的傳輸問題。因為一般浮點數都是32位&#xff0c;而Modbus總線中只能傳輸最多16位的數據。解決方法&#xff1a;可以利用兩個整形數傳送一個浮點數&#xff08;即將一個32位的二進制數分割成兩個…

服務器虛擬化網口,服務器安裝虛擬網口

服務器安裝虛擬網口 內容精選換一換Atlas 800 訓練服務器(型號 9010)安裝上架、服務器基礎參數配置、安裝操作系統等操作請參見《Atlas 800 訓練服務器 用戶指南 (型號9010)》。Atlas 800 訓練服務器(型號 9010)適配操作系統如表1所示。請參考表2下載驅動和固件包。Atlas 800 訓…

芒果云接嗎_芒果糯米飯是生產力的關鍵嗎?

芒果云接嗎Would you like to know how your mood impact your sleep and how your parents influence your happiness levels?您想知道您的心情如何影響您的睡眠以及您的父母如何影響您的幸福感嗎&#xff1f; Become a data nerd, and track it!成為數據書呆子&#xff0c;…

hdoj4283 You Are the One

題意&#xff1a;每個人出場時獲得等待時間*值的unhappy值。有個棧換出場順序。問怎樣最小&#xff1f; 一開始的時候覺得在中間取斷點&#xff0c;dp[i][j]表示區間全出場后的最小值。那么dp[i][j]dp[i][k]dp[k1][j]&#xff0c;但這樣是不行的。因為有可能最優解是[i][k]只出…

laravel-admin 開發 bootstrap-treeview 擴展包

laravel-admin 擴展開發文檔https://laravel-admin.org/doc... 效果圖&#xff1a; 開發過程&#xff1a; 1、先創建Laravel項目&#xff0c;并集成laravel-admin&#xff0c;教程&#xff1a; http://note.youdao.com/notesh... 2、生成開發擴展包 php artisan admin:extend c…