Tornado路由与Application的实现
作者:tracy小猫
路由原理
在Tornado框架中,路由是指将请求的URL映射到对应的处理函数上,这个过程需要通过正则表达式来实现。Tornado使用了一种叫做Application的类来封装整个Web应用程序.我们定制一个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基础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 类,并在初始化中进行了一些准备工作。
首先,定义了几个实例变量,包括 ops_mongo(mongoDB 数据库连接)、redis_cluster(基于 redis 的 session)、redis_manager(管理 redis 连接)和 redis(redis 数据库连接)。
然后,根据配置文件中的设置,设定了 Tornado Application 的一些参数,并根据路由设置得到路由列表。
接下来,调用了 prepare 方法,该方法中通过异步方法初始化了 ops_mongo(mongoDB 数据库连接)、redis_cluster(基于 redis 的 session)和 redis(redis 数据库连接),并创建了 redis_manager 实例管理 redis 连接。最后,调用了异步方法 create_all_indexes,创建了所有的索引。
最后,定义了一个异步方法 on_close,在 Web 服务关闭时关闭了 redis_manager 实例和 ops_mongo 实例。同时加入了一些对于关闭的日志信息。
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 的状态码和 Not Found 的原因。
- write_error 方法会在遇到未处理的异常时自动调用,可以自定义错误响应的方式。这里定义的行为是结束响应并返回一个包含错误信息的 JSON 对象
另外,getRoutes 方法定义了一个以元组形式表示的 URL 路由表,将 /redis 映射到名为 TestRedisHandler 的类。
RequestHandler的功能
在Tornado中,RequestHandler是处理每个HTTP请求的基础类,所有的请求处理类都应该继承自它。RequestHandler有如下几个常用的方法:
- initialize():初始化方法,在请求处理类实例化时自动调用,用于设置一些参数或进行一些初始化操作。
- prepare():在处理请求前调用,用于进行一些安全检查、身份验证等操作,并且可以自定义错误页面。
- get()、post()等:根据HTTP请求方式的不同,提供相应的get()、post()等方法来处理请求,并返回相应的HTTP响应。
- write()、finish():用于返回响应数据,并结束请求处理。
- redirect()、set_header()等:提供一些常用的HTTP响应辅助方法。
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): *"""* **返回统一的*json*格式结果 *****: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): # 处理完请求后输出日志,将请求头信息和响应结果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 两个类。该类提供了一些基础方法,可以被继承使用来构建业务处理类。
这个类定义了两种返回方式:普通返回和分页返回。普通返回返回的数据格式有 code、message、tid 和 data 四个字段,其中 code 表示请求状态码,message 表示请求状态消息,tid 表示请求追踪 id,data 表示响应数据。分页返回在普通返回的基础上加入了分页参数信息,即 pageSize、pages、items 和 pageNo。
该类实现了三个类方法:return_response、return_page_response 和 create_page。return_response 返回普通返回的响应对象,return_page_response 返回分页返回的响应对象,create_page 用于创建分页响应。
该类实现了三个实例方法:get_request、return_json 和 write_error。get_request 方法用于获取请求体的 json 数据,return_json 方法用于返回 json 格式的响应,write_error 方法用于处理请求出错时返回的响应。
该类实现了两个钩子函数:prepare 和 on_finish。prepare 在请求进入处理前被调用,可以用来获取请求头信息等,on_finish 在请求处理完后被调用,用于记录日志等操作。
处理错误
在Tornado中,异常处理同样也是一个非常重要的问题,异常处理可以让我们在遇到错误时能够有更好的反应和处理。可以通过使用@tornado.web.appadvice()装饰器或者重写write_error()方法来进行自定义错误处理。
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的默认错误处理方式。当我们访问http://localhost:8888/err?error=syntax 时,会输出 Some Exception occurs: There is a syntax error in your code
处理完成
tornado的RequestHandler是用于请求处理的基本类,其中on_finish是RequestHandler的一个方法,可以在请求处理完成后被调用。当客户端请求处理完成时,可以使用此方法进行一些清理工作或日志记录等操作,并将tornado 对象释放回底层IOLoop。在on_finish方法内可以进行资源释放、统计请求处理时间等工作。此外,在建立连接之前初始化某些对象或资源,也可以在on_finish方法中完成清理。
以下是RequestHandler的on_finish方法的声明:
def on_finish(self) -> None: """ Finish this response, ending the HTTP request. """ pass
Tornado提供了很多的错误处理接口来帮助我们进行异常处理。为了使Web程序更加的健壮,我们需要尽可能的考虑到各种可能出现的错误情况,并对异常情况进行适当的处理。
到此这篇关于Tornado路由与Application的实现的文章就介绍到这了,更多相关Tornado路由与Application内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!