Tornado路由與Application的實現(xiàn)
路由原理
在Tornado框架中,路由是指將請求的URL映射到對應(yīng)的處理函數(shù)上,這個過程需要通過正則表達(dá)式來實現(xiàn)。Tornado使用了一種叫做Application的類來封裝整個Web應(yīng)用程序.我們定制一個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")
這段代碼定義了一個繼承自 Tornado Application 的自定義 Application 類,并在初始化中進(jìn)行了一些準(zhǔn)備工作。
首先,定義了幾個實例變量,包括 ops_mongo(mongoDB 數(shù)據(jù)庫連接)、redis_cluster(基于 redis 的 session)、redis_manager(管理 redis 連接)和 redis(redis 數(shù)據(jù)庫連接)。
然后,根據(jù)配置文件中的設(shè)置,設(shè)定了 Tornado Application 的一些參數(shù),并根據(jù)路由設(shè)置得到路由列表。
接下來,調(diào)用了 prepare 方法,該方法中通過異步方法初始化了 ops_mongo(mongoDB 數(shù)據(jù)庫連接)、redis_cluster(基于 redis 的 session)和 redis(redis 數(shù)據(jù)庫連接),并創(chuàng)建了 redis_manager 實例管理 redis 連接。最后,調(diào)用了異步方法 create_all_indexes,創(chuàng)建了所有的索引。
最后,定義了一個異步方法 on_close,在 Web 服務(wù)關(guān)閉時關(guān)閉了 redis_manager 實例和 ops_mongo 實例。同時加入了一些對于關(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 框架定義一個繼承自 tornado.web.RequestHandler 的 DefaultHandler 類,包含兩個方法 get 和 write_error。
- get 方法會拋出一個 HTTPError,表示請求的頁面不存在或其他錯誤,會返回一個 404 的狀態(tài)碼和 Not Found 的原因。
- write_error 方法會在遇到未處理的異常時自動調(diào)用,可以自定義錯誤響應(yīng)的方式。這里定義的行為是結(jié)束響應(yīng)并返回一個包含錯誤信息的 JSON 對象
另外,getRoutes 方法定義了一個以元組形式表示的 URL 路由表,將 /redis 映射到名為 TestRedisHandler 的類。
RequestHandler的功能
在Tornado中,RequestHandler是處理每個HTTP請求的基礎(chǔ)類,所有的請求處理類都應(yīng)該繼承自它。RequestHandler有如下幾個常用的方法:
- initialize():初始化方法,在請求處理類實例化時自動調(diào)用,用于設(shè)置一些參數(shù)或進(jìn)行一些初始化操作。
- prepare():在處理請求前調(diào)用,用于進(jìn)行一些安全檢查、身份驗證等操作,并且可以自定義錯誤頁面。
- get()、post()等:根據(jù)HTTP請求方式的不同,提供相應(yīng)的get()、post()等方法來處理請求,并返回相應(yīng)的HTTP響應(yīng)。
- write()、finish():用于返回響應(yīng)數(shù)據(jù),并結(jié)束請求處理。
- redirect()、set_header()等:提供一些常用的HTTP響應(yīng)輔助方法。
class BaseHandler(RequestHandler, ABC): ????traceid = None ????start_time = None ????# 普通返回 ????response_dict = {'code': 0, 'message': "", 'tid': "", 'data': None} ????# 分頁返回 ????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): ????????# 處理完請求后輸出日志,將請求頭信息和響應(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 兩個類。該類提供了一些基礎(chǔ)方法,可以被繼承使用來構(gòu)建業(yè)務(wù)處理類。
這個類定義了兩種返回方式:普通返回和分頁返回。普通返回返回的數(shù)據(jù)格式有 code、message、tid 和 data 四個字段,其中 code 表示請求狀態(tài)碼,message 表示請求狀態(tài)消息,tid 表示請求追蹤 id,data 表示響應(yīng)數(shù)據(jù)。分頁返回在普通返回的基礎(chǔ)上加入了分頁參數(shù)信息,即 pageSize、pages、items 和 pageNo。
該類實現(xiàn)了三個類方法:return_response、return_page_response 和 create_page。return_response 返回普通返回的響應(yīng)對象,return_page_response 返回分頁返回的響應(yīng)對象,create_page 用于創(chuàng)建分頁響應(yīng)。
該類實現(xiàn)了三個實例方法:get_request、return_json 和 write_error。get_request 方法用于獲取請求體的 json 數(shù)據(jù),return_json 方法用于返回 json 格式的響應(yīng),write_error 方法用于處理請求出錯時返回的響應(yīng)。
該類實現(xiàn)了兩個鉤子函數(shù):prepare 和 on_finish。prepare 在請求進(jìn)入處理前被調(diào)用,可以用來獲取請求頭信息等,on_finish 在請求處理完后被調(diào)用,用于記錄日志等操作。
處理錯誤
在Tornado中,異常處理同樣也是一個非常重要的問題,異常處理可以讓我們在遇到錯誤時能夠有更好的反應(yīng)和處理。可以通過使用@tornado.web.appadvice()裝飾器或者重寫write_error()方法來進(jìn)行自定義錯誤處理。
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)錯誤處理方式。當(dāng)我們訪問http://localhost:8888/err?error=syntax 時,會輸出 Some Exception occurs: There is a syntax error in your code
處理完成
tornado的RequestHandler是用于請求處理的基本類,其中on_finish是RequestHandler的一個方法,可以在請求處理完成后被調(diào)用。當(dāng)客戶端請求處理完成時,可以使用此方法進(jìn)行一些清理工作或日志記錄等操作,并將tornado 對象釋放回底層IOLoop。在on_finish方法內(nèi)可以進(jìn)行資源釋放、統(tǒng)計請求處理時間等工作。此外,在建立連接之前初始化某些對象或資源,也可以在on_finish方法中完成清理。
以下是RequestHandler的on_finish方法的聲明:
def on_finish(self) -> None: ????????""" ????????Finish this response, ending the HTTP request. ????????""" ????????pass
Tornado提供了很多的錯誤處理接口來幫助我們進(jìn)行異常處理。為了使Web程序更加的健壯,我們需要盡可能的考慮到各種可能出現(xiàn)的錯誤情況,并對異常情況進(jìn)行適當(dāng)?shù)奶幚怼?/p>
到此這篇關(guān)于Tornado路由與Application的實現(xiàn)的文章就介紹到這了,更多相關(guān)Tornado路由與Application內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python實現(xiàn)sublime3的less編譯插件示例
這篇文章主要介紹了python實現(xiàn)sublime3的less編譯插件示例的相關(guān)資料2014-04-04在arcgis使用python腳本進(jìn)行字段計算時是如何解決中文問題的
這篇文章主要介紹了在arcgis使用python腳本進(jìn)行字段計算時是如何解決中文問題的,需要的朋友可以參考下2015-10-10控制Python浮點數(shù)輸出位數(shù)的操作方法
在python的輸出結(jié)果中,尤其是浮點數(shù)的輸出,當(dāng)我們需要寫入文本文件時,最好是采用統(tǒng)一的輸出格式,這樣也能夠增強結(jié)果的可讀性,這篇文章主要介紹了控制Python浮點數(shù)輸出位數(shù)的方法,需要的朋友可以參考下2022-04-04Python中使用bidict模塊雙向字典結(jié)構(gòu)的奇技淫巧
bidict模塊通過一對一映射結(jié)構(gòu)的處理為Pyhton帶來雙向字典,能夠更加利用Python的切片功能,這里我們就來學(xué)習(xí)Python中使用bidict模塊雙向字典結(jié)構(gòu)的奇技淫巧:2016-07-07Windows 平臺做 Python 開發(fā)的最佳組合(推薦)
在 Windows 上如何做 Python 開發(fā)呢?相信大神們都會有自己的解決方案,但本文希望介紹微軟官方發(fā)布的 Terminal 和 Visual Studio Code,希望它們能構(gòu)建更流暢的 Windows 開發(fā)體驗,感興趣的朋友跟隨小編一起看看吧2020-07-07