python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Tornado路由与Application

Tornado路由与Application的实现

作者:tracy小猫

本文主要介绍了Tornado路由与Application的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

路由原理

在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。

另外,getRoutes 方法定义了一个以元组形式表示的 URL 路由表,将 /redis 映射到名为 TestRedisHandler 的类。

RequestHandler的功能

在Tornado中,RequestHandler是处理每个HTTP请求的基础类,所有的请求处理类都应该继承自它。RequestHandler有如下几个常用的方法:

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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文