Tornado路由与Application的实现

作者:tracy小猫 时间:2021-01-26 19:56:59 

路由原理

在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程序更加的健壮,我们需要尽可能的考虑到各种可能出现的错误情况,并对异常情况进行适当的处理。

来源:https://juejin.cn/post/7232824323753001015

标签:Tornado,路由,Application
0
投稿

猜你喜欢

  • windows10 pycharm下安装pyltp库和加载模型实现语义角色标注的示例代码

    2021-11-09 10:58:04
  • 对Python字符串中的换行符和制表符介绍

    2021-11-11 15:45:29
  • 详解用pyecharts Geo实现动态数据热力图城市找不到问题解决

    2022-02-04 14:10:29
  • Python3 SSH远程连接服务器的方法示例

    2021-02-15 17:50:05
  • 再谈CSS样式表书写风格

    2009-03-30 16:09:00
  • ProC 连接Oracle代码

    2009-06-10 18:12:00
  • jdbc连接sqlserver数据库示例

    2024-01-17 12:56:16
  • 浅谈JavaScript函数参数的可修改性问题

    2024-05-02 17:20:29
  • Python微服务开发之使用FastAPI构建高效API

    2022-04-20 21:51:13
  • SQL Data Services将成为云中完整的数据库

    2009-03-25 12:28:00
  • 搜索系统与导航系统的关系

    2009-09-08 12:44:00
  • 深入理解ES6中let和闭包

    2024-05-28 15:41:25
  • Python上下文管理器深入讲解

    2022-02-23 08:50:02
  • MySQL中多个left join on关联条件的顺序说明

    2024-01-15 14:01:46
  • python如何将两个txt文件内容合并

    2021-09-03 22:04:52
  • 你应该知道的Python3.6、3.7、3.8新特性小结

    2023-10-14 02:18:37
  • MySQL8数据库安装及SQL语句详解

    2024-01-17 21:25:33
  • css去掉checkbox边框的方法

    2011-06-06 10:32:00
  • Go语法糖之‘...’ 的使用实例详解

    2024-04-26 17:16:43
  • python之dlib包安装失败问题及解决

    2022-11-20 09:33:55
  • asp之家 网络编程 m.aspxhome.com