Tornado路由與Application的實(shí)現(xiàn)
路由原理
在Tornado框架中,路由是指將請(qǐng)求的URL映射到對(duì)應(yīng)的處理函數(shù)上,這個(gè)過(guò)程需要通過(guò)正則表達(dá)式來(lái)實(shí)現(xiàn)。Tornado使用了一種叫做Application的類來(lái)封裝整個(gè)Web應(yīng)用程序.我們定制一個(gè)Applicaiton繼承tornado的Application
from tornado.web import Application as tornadoApp
class Application(tornadoApp): ? ? """ 定制 Tornado Application 集成日志、sqlalchemy 等功能 """ ? ? def __init__(self): ? ? ? ? self.ops_mongo = None ?# ops mongo ? ? ? ? self.redis_cluster = None ?# redis基礎(chǔ)session ? ? ? ? self.redis_manager = None ?# redis_manager session ? ? ? ? self.redis = None ? ? ? ? tornado_settings = { ? ? ? ? ? ? 'autoreload': settings.service.server.autoreload, ? ? ? ? ? ? 'debug': settings.service.server.debug, ? ? ? ? ? ? "default_handler_class": DefaultHandler, ? ? ? ? } ? ? ? ? route = getRoutes(settings) ? ? ? ? self.prepare() ? ? ? ? super(Application, self).__init__(handlers=route, **tornado_settings) ? ? def prepare(self): ? ? ? ? self.redis_cluster = asyncio.get_event_loop().run_until_complete( ? ? ? ? ? ? init_cluster() ? ? ? ? ) ? ? ? ? self.ops_mongo = asyncio.get_event_loop().run_until_complete( ? ? ? ? ? ? init_ops_mongodb() ? ? ? ? ) ? ? ? ? loop = asyncio.get_event_loop() ? ? ? ? self.redis = RedisPool(loop=loop).get_conn() ? ? ? ? self.redis_manager = RedisManager(self.redis) ? ? ? ? asyncio.get_event_loop().run_until_complete( ? ? ? ? ? ? create_all_indexes() ? ? ? ? ) ? ? async def on_close(self, server_conn: object) -> None: ? ? ? ? await self.redis_manager.close() ? ? ? ? logger.info("close redis") ? ? ? ? self.ops_mongo.close() ? ? ? ? logger.info("close mongo")
這段代碼定義了一個(gè)繼承自 Tornado Application 的自定義 Application 類,并在初始化中進(jìn)行了一些準(zhǔn)備工作。
首先,定義了幾個(gè)實(shí)例變量,包括 ops_mongo(mongoDB 數(shù)據(jù)庫(kù)連接)、redis_cluster(基于 redis 的 session)、redis_manager(管理 redis 連接)和 redis(redis 數(shù)據(jù)庫(kù)連接)。
然后,根據(jù)配置文件中的設(shè)置,設(shè)定了 Tornado Application 的一些參數(shù),并根據(jù)路由設(shè)置得到路由列表。
接下來(lái),調(diào)用了 prepare 方法,該方法中通過(guò)異步方法初始化了 ops_mongo(mongoDB 數(shù)據(jù)庫(kù)連接)、redis_cluster(基于 redis 的 session)和 redis(redis 數(shù)據(jù)庫(kù)連接),并創(chuàng)建了 redis_manager 實(shí)例管理 redis 連接。最后,調(diào)用了異步方法 create_all_indexes,創(chuàng)建了所有的索引。
最后,定義了一個(gè)異步方法 on_close,在 Web 服務(wù)關(guān)閉時(shí)關(guān)閉了 redis_manager 實(shí)例和 ops_mongo 實(shí)例。同時(shí)加入了一些對(duì)于關(guān)閉的日志信息。
class DefaultHandler(tornado.web.RequestHandler): ? ? def get(self): ? ? ? ? raise tornado.web.HTTPError( ? ? ? ? ? ? status_code=404, ? ? ? ? ? ? reason="Not Found" ? ? ? ? ) ? ? def write_error(self, status_code, exc_info=None, **kwargs): ? ? ? ? self.finish({"error": self._reason}) def getRoutes(setting): ? ? Routes = [ ? ? ? ? (r"/redis", home.TestRedisHandler), ? ? ] ? ? return Routes
該代碼是使用 Tornado 框架定義一個(gè)繼承自 tornado.web.RequestHandler 的 DefaultHandler 類,包含兩個(gè)方法 get 和 write_error。
- get 方法會(huì)拋出一個(gè) HTTPError,表示請(qǐng)求的頁(yè)面不存在或其他錯(cuò)誤,會(huì)返回一個(gè) 404 的狀態(tài)碼和 Not Found 的原因。
- write_error 方法會(huì)在遇到未處理的異常時(shí)自動(dòng)調(diào)用,可以自定義錯(cuò)誤響應(yīng)的方式。這里定義的行為是結(jié)束響應(yīng)并返回一個(gè)包含錯(cuò)誤信息的 JSON 對(duì)象
另外,getRoutes 方法定義了一個(gè)以元組形式表示的 URL 路由表,將 /redis 映射到名為 TestRedisHandler 的類。
RequestHandler的功能
在Tornado中,RequestHandler是處理每個(gè)HTTP請(qǐng)求的基礎(chǔ)類,所有的請(qǐng)求處理類都應(yīng)該繼承自它。RequestHandler有如下幾個(gè)常用的方法:
- initialize():初始化方法,在請(qǐng)求處理類實(shí)例化時(shí)自動(dòng)調(diào)用,用于設(shè)置一些參數(shù)或進(jìn)行一些初始化操作。
- prepare():在處理請(qǐng)求前調(diào)用,用于進(jìn)行一些安全檢查、身份驗(yàn)證等操作,并且可以自定義錯(cuò)誤頁(yè)面。
- get()、post()等:根據(jù)HTTP請(qǐng)求方式的不同,提供相應(yīng)的get()、post()等方法來(lái)處理請(qǐng)求,并返回相應(yīng)的HTTP響應(yīng)。
- write()、finish():用于返回響應(yīng)數(shù)據(jù),并結(jié)束請(qǐng)求處理。
- redirect()、set_header()等:提供一些常用的HTTP響應(yīng)輔助方法。
class BaseHandler(RequestHandler, ABC): ????traceid = None ????start_time = None ????# 普通返回 ????response_dict = {'code': 0, 'message': "", 'tid': "", 'data': None} ????# 分頁(yè)返回 ????response_page_dict = {'code': 0, 'message': "", 'tid': "", 'data': None, "pagination": ????????{"pagesize": 0, "pages": 0, "items": 0, "pageno": 0} ??????????????????????????} ????@classmethod ????def return_response(cls): ????????resp = AttrDict(cls.response_dict) ????????return resp ????@classmethod ????def return_page_response(cls): ????????resp = AttrDict(cls.response_page_dict) ????????return resp ????@classmethod ????def create_page(cls, response: AttrDict, total=None, pagesize=0, pageno=None): ????????response.pagination["items"] = total ????????response.pagination["pagesize"] = pagesize ????????response.pagination["pages"] = total // pagesize if total % pagesize == 0 else total // pagesize + 1 ????????response.pagination["pageno"] = pageno ????????return response ????def get_request(self): ????????request_body = None ????????if self.request.body: ????????????request_body = tornado.escape.json_decode(self.request.body) ????????return request_body ????def return_json(self, response): ????????*"""* **返回統(tǒng)一的*json*格式結(jié)果 *****:param*** *response:* *"""* ** ? **self.set_header("Content-Type", "application/json; charset=UTF-8") ????????self.response_dict = json.dumps(response, default=json_util.default) ????????self.write(self.response_dict) ????def prepare(self): ????????trace_id = self.request.headers.get("tid", None) ????????self.__class__.start_time = datetime.datetime.now() ????????if not trace_id: ????????????trace_id = str(uuid.uuid4()) ????????????self.request.headers["tid"] = trace_id ????????self.__class__.traceid = trace_id ????def write_error(self, status_code, exc_info=None, api_code=None, **kwargs): ????????resp = self.return_response() ????????resp.code = status_code ????????resp.tid = self.traceid ????????ex = exc_info[1] ????????if isinstance(ex, Exception): ????????????err_msg = str(ex) ????????????resp.message = err_msg ????????if isinstance(ex, tornado.web.HTTPError): ????????????err_msg = str(ex) ????????????resp.message = err_msg ????????if isinstance(ex, tornado.web.HTTPError) and ex.log_message: ????????????err_msg = str(ex.log_message) ????????????resp.message = err_msg ????????self.response_dict = json.dumps(resp, default=json_util.default) ????????self.finish(resp) ????def on_finish(self): ????????# 處理完請(qǐng)求后輸出日志,將請(qǐng)求頭信息和響應(yīng)結(jié)果json化 ????????end_time = datetime.datetime.now() ????????request_data = { ????????????"tid": self.request.headers.get("tid", None), ????????????"url": self.request.uri, ????????????"method": self.request.method, ????????????"remote_ip": self.request.remote_ip, ????????????"user_agent": self.request.headers["User-Agent"], ????????????"request_body": json.loads(self.request.body) if self.request.body else "" ????????} ????????response_data = { ????????????"status": self.get_status(), ????????????"response_body": json.loads(self.response_dict) if not isinstance(self.response_dict, ??????????????????????????????????????????????????????????????????????????????dict) else self.response_dict, ????????????"response_time": (end_time - self.start_time).microseconds / 1000 ????????} ????????logger = get_logger("access") ????????logger.bind(**request_data).bind(**response_data).info("request success")
BaseHandler 的類,它繼承了 tornado.web.RequestHandler 和 abc.ABC 兩個(gè)類。該類提供了一些基礎(chǔ)方法,可以被繼承使用來(lái)構(gòu)建業(yè)務(wù)處理類。
這個(gè)類定義了兩種返回方式:普通返回和分頁(yè)返回。普通返回返回的數(shù)據(jù)格式有 code、message、tid 和 data 四個(gè)字段,其中 code 表示請(qǐng)求狀態(tài)碼,message 表示請(qǐng)求狀態(tài)消息,tid 表示請(qǐng)求追蹤 id,data 表示響應(yīng)數(shù)據(jù)。分頁(yè)返回在普通返回的基礎(chǔ)上加入了分頁(yè)參數(shù)信息,即 pageSize、pages、items 和 pageNo。
該類實(shí)現(xiàn)了三個(gè)類方法:return_response、return_page_response 和 create_page。return_response 返回普通返回的響應(yīng)對(duì)象,return_page_response 返回分頁(yè)返回的響應(yīng)對(duì)象,create_page 用于創(chuàng)建分頁(yè)響應(yīng)。
該類實(shí)現(xiàn)了三個(gè)實(shí)例方法:get_request、return_json 和 write_error。get_request 方法用于獲取請(qǐng)求體的 json 數(shù)據(jù),return_json 方法用于返回 json 格式的響應(yīng),write_error 方法用于處理請(qǐng)求出錯(cuò)時(shí)返回的響應(yīng)。
該類實(shí)現(xiàn)了兩個(gè)鉤子函數(shù):prepare 和 on_finish。prepare 在請(qǐng)求進(jìn)入處理前被調(diào)用,可以用來(lái)獲取請(qǐng)求頭信息等,on_finish 在請(qǐng)求處理完后被調(diào)用,用于記錄日志等操作。
處理錯(cuò)誤
在Tornado中,異常處理同樣也是一個(gè)非常重要的問(wèn)題,異常處理可以讓我們?cè)谟龅藉e(cuò)誤時(shí)能夠有更好的反應(yīng)和處理??梢酝ㄟ^(guò)使用@tornado.web.appadvice()裝飾器或者重寫write_error()方法來(lái)進(jìn)行自定義錯(cuò)誤處理。
import tornado.web class CustomErrorHandler(tornado.web.RequestHandler): ????def write_error(self, status_code, **kwargs): ????????if self.settings.get("serve_traceback"): ????????????self.write('Some Exception occurs: {}'.format(kwargs['exc_info'][1])) ????????else: ????????????self.write('Some Exception occurs. We are on it!') ????????self.set_status(status_code) class ErrorExample(tornado.web.RequestHandler): ????def get(self, error_printer): ????????if error_printer == "raise": ????????????raise Exception("An unknown error occurred.") ????????elif error_printer == "syntax": ????????????raise SyntaxError("There is a syntax error in your code.") ????????else: ????????????self.write("This is normal operation!")
上述代碼中,使用write_error()重寫了RequestHandler的默認(rèn)錯(cuò)誤處理方式。當(dāng)我們?cè)L問(wèn)http://localhost:8888/err?error=syntax 時(shí),會(huì)輸出 Some Exception occurs: There is a syntax error in your code
處理完成
tornado的RequestHandler是用于請(qǐng)求處理的基本類,其中on_finish是RequestHandler的一個(gè)方法,可以在請(qǐng)求處理完成后被調(diào)用。當(dāng)客戶端請(qǐng)求處理完成時(shí),可以使用此方法進(jìn)行一些清理工作或日志記錄等操作,并將tornado 對(duì)象釋放回底層IOLoop。在on_finish方法內(nèi)可以進(jìn)行資源釋放、統(tǒng)計(jì)請(qǐng)求處理時(shí)間等工作。此外,在建立連接之前初始化某些對(duì)象或資源,也可以在on_finish方法中完成清理。
以下是RequestHandler的on_finish方法的聲明:
def on_finish(self) -> None: ????????""" ????????Finish this response, ending the HTTP request. ????????""" ????????pass
Tornado提供了很多的錯(cuò)誤處理接口來(lái)幫助我們進(jìn)行異常處理。為了使Web程序更加的健壯,我們需要盡可能的考慮到各種可能出現(xiàn)的錯(cuò)誤情況,并對(duì)異常情況進(jìn)行適當(dāng)?shù)奶幚怼?/p>
到此這篇關(guān)于Tornado路由與Application的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Tornado路由與Application內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python實(shí)現(xiàn)sublime3的less編譯插件示例
這篇文章主要介紹了python實(shí)現(xiàn)sublime3的less編譯插件示例的相關(guān)資料2014-04-04在arcgis使用python腳本進(jìn)行字段計(jì)算時(shí)是如何解決中文問(wèn)題的
這篇文章主要介紹了在arcgis使用python腳本進(jìn)行字段計(jì)算時(shí)是如何解決中文問(wèn)題的,需要的朋友可以參考下2015-10-10控制Python浮點(diǎn)數(shù)輸出位數(shù)的操作方法
在python的輸出結(jié)果中,尤其是浮點(diǎn)數(shù)的輸出,當(dāng)我們需要寫入文本文件時(shí),最好是采用統(tǒng)一的輸出格式,這樣也能夠增強(qiáng)結(jié)果的可讀性,這篇文章主要介紹了控制Python浮點(diǎn)數(shù)輸出位數(shù)的方法,需要的朋友可以參考下2022-04-04Python實(shí)現(xiàn)合成多張圖片到PDF格式
在日常生活中,經(jīng)常會(huì)遇到需要提交身份證正反面證明資料的情況,而且這些網(wǎng)站大部分只接受pdf格式,這時(shí)候我們就需要把身份證正反面兩張圖片合成為一個(gè)pdf文件。本文將為大家提供用Python實(shí)現(xiàn)這一要求的方法,需要的可以參考一下2022-02-02python實(shí)現(xiàn)與redis交互操作詳解
這篇文章主要介紹了python實(shí)現(xiàn)與redis交互操作,結(jié)合實(shí)例形式分析了Python Redis模塊的安裝、導(dǎo)入、連接與簡(jiǎn)單操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2020-04-04Python中使用bidict模塊雙向字典結(jié)構(gòu)的奇技淫巧
bidict模塊通過(guò)一對(duì)一映射結(jié)構(gòu)的處理為Pyhton帶來(lái)雙向字典,能夠更加利用Python的切片功能,這里我們就來(lái)學(xué)習(xí)Python中使用bidict模塊雙向字典結(jié)構(gòu)的奇技淫巧:2016-07-07Python 通過(guò)分隔符分割文件后按特定次序重新組合的操作
這篇文章主要介紹了Python 通過(guò)分隔符分割文件后按特定次序重新組合的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04Windows 平臺(tái)做 Python 開發(fā)的最佳組合(推薦)
在 Windows 上如何做 Python 開發(fā)呢?相信大神們都會(huì)有自己的解決方案,但本文希望介紹微軟官方發(fā)布的 Terminal 和 Visual Studio Code,希望它們能構(gòu)建更流暢的 Windows 開發(fā)體驗(yàn),感興趣的朋友跟隨小編一起看看吧2020-07-07python?離散點(diǎn)圖畫法的實(shí)現(xiàn)
本文主要介紹了python?離散點(diǎn)圖畫法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04