zoukankan      html  css  js  c++  java
  • tornado 学习笔记7 RequestHandler功能分析

           在第5部分讲到,构建一个tornado网站,必须包含一个或者多个handler,这些handler是RequestHandler的子类。每个请求都会被映射到handler中进行处理,处理后再将结果返回给客户端。所以,可以看到hanlder作为客户端请求跟业务服务逻辑间的桥梁,如果拿MVC的模式来类比的话,每个handler就相当于MVC中的Controller。

           RequestHanlder作为所有hanlder的父类,我们看看他有哪些方法与接口,子类需要怎样继承?

    7.1构造函数

    定义:

    def __init__(self, application, request, **kwargs):

    参数:

          application: Application对象

          request: request请求对象

           kwargs:其他参数,在hanlder映射配置时,可以设置。

    处理过程:

    super(RequestHandler, self).__init__()
    
    self.application = application
    self.request = request
    self._headers_written = False
    self._finished = False
    self._auto_finish = True
    self._transforms = None  # will be set in _execute
    self._prepared_future = None
    self.path_args = None
    self.path_kwargs = None
    self.ui = ObjectDict((n, self._ui_method(m)) for n, m in
                         application.ui_methods.items())
    # UIModules are available as both `modules` and `_tt_modules` in the
    # template namespace.  Historically only `modules` was available
    # but could be clobbered by user additions to the namespace.
    # The template {% module %} directive looks in `_tt_modules` to avoid
    # possible conflicts.
    self.ui["_tt_modules"] = _UIModuleNamespace(self,
                                                application.ui_modules)
    self.ui["modules"] = self.ui["_tt_modules"]
    self.clear()
    self.request.connection.set_close_callback(self.on_connection_close)
    self.initialize(**kwargs)
    1. 对外部属性跟内部属性(带下划线)的赋值。
    2. 然后对ui的处理,这个暂时不做深入细读。
    3. self.clear() 调用clear方法,初始化一些属性。
    4. 设置请求连接关闭时的回调函数。
              self.request.connection.set_close_callback(self.on_connection_close)

       5.    调用初始化函数。

              self.initialize(**kwargs)

           这个被子类继承,针对每个hanlder实现自己的初始化过程。

    入点函数

    7.2 initialize方法

          该方法被子类重写,实现初始化过程。参数的来源于配置Application时,传递的参数。举例如下:

    # -*- coding: utf-8 -*-
    from tornado.ioloop import IOLoop
    from tornado.web import Application, RequestHandler
    
    __author__ = 'Administrator'
    
    
    class BookRequestHandler(RequestHandler):
        def initialize(self, welcome):
            self.welcome = welcome
    
        def get(self, *args, **kwargs):
            print(self.welcome)
            self.write(self.welcome)
    
    welcome = "**大学图书馆"
    app = Application(handlers=[
        (r"/book", BookRequestHandler, dict(welcome=welcome)),
    ])
    
    
    def main():
        app.listen(8888)
        IOLoop.current().start()
    
    if __name__ == "__main__":
        main()
             

              将welcome初始化的值传递到BookRequestHandler的self.welcome属性中。当访问http://localhost:8888/book时,打印出welcome的值。

              结果如下图:

    clip_image002

    7.3 prepare 、 on_finish方法

              prepare方法用于当真正调用请求处理方法之前的初始化处理,比如get、post方法。而on_finish用于请求处理结束后的一些清理工作。这两个方法一个在处理前,一个在处理后,可以根据实际需要进行重写。比如用prepare方法做些初始化操作(比如赋值设置全局变量,比如打开I/O),而on_finish方法可以做一些清理对象占用的内存或者关闭数据库连接等等。举个例子,来证明他们的执行顺序。

    class BookRequestHandler(RequestHandler):
        def initialize(self, welcome,value2):
            print("initialize method:initilize some variables....")
            self.welcome = welcome
            self.value2=value2
    
        def get(self, *args, **kwargs):
            #print(self.welcome + "
    " + "and value2 =" + self.value2)
            print("get method: Processing get Method...........")
            self.write(self.welcome + "
    " + "and value2 =" + self.value2)
    
        def set_default_headers(self):
            self._headers.add("custom_header1", "custom_header1")
            self._headers.add("custom_header2", "custom_header2")
    
        def prepare(self):
            print("prepare method:initilize some variables....")
    
        def on_finish(self):
            print("on_finish method:clear some memory or close database connection")

          执行的结果如下:

    clip_image002[5]

           所以得出执行的顺序为:

           initialize > prepare > get > on_finish

           如果有熟悉Java 的JUnit的话呢,prepare跟on_finish方法相当于before跟behind两个注解的功能。

    获得输入的函数:

    7.4  get_argument、get_arguments方法

            返回给定参数的值,get_argument获得单个值,而get_arguments是针对参数存在多个值得情况下使用,返回多个值的列表。看一get_arguments方法的源代码,如下:

    clip_image004

              它的实现是调用了内部方法_get_arguments,注意传递的参数self.request.arguments,从request(HTTPServerRequest对象)的arguments属性中去查询给定名称的值。看看HTTPServerRequest源代码(位于tornado>httputil.py)对arguments的解释,如下截图:

    clip_image006

              大体意思就是说,这里存储的是客户端GET/POST方法提交上来的合并后的参数列表集合。也就是说RequestHanlder的get_arguments方法是能获得不管是Get还是POST得参数的值。举个GET提交参数的例子,

              修改BookRequestHanlder的get方法。如下:

    def get(self, *args, **kwargs):
        #print(self.welcome + "
    " + "and value2 =" + self.value2)
        print("get method: Processing get Method...........")
        #self.write(self.welcome + "
    " + "and value2 =" + self.value2)
        self.write("参数name的值为:" + self.get_argument("name", "liaofei"))

           向游览器中打印出参数name的值,游览器中访问:http://localhost:8888/book?name=brain,结果如下图所示:

    clip_image008

              在举个POST方式提交参数的例子,在BookRequestHanlder 中新增POST方法,如下:

    def post(self, *args, **kwargs):
        print(self.request.arguments)
        print("POS age:" + self.get_argument("age"))
        self.write("POS age:" + self.get_argument("age"))

            网上下载一个模拟工具进行POST数据提交,我找了很久用了,找到如下工具(很多工具不能用,这个稍微能用一下),

    clip_image010

              后台打印的结果为:clip_image012,HTTPRequest 的arguments属性是一个字典。

             提一个问题?如果URL中带有age查询参数,而post过去也有age参数,这时HTTPRequest 的arguments中age的值会是什么???测试一下便知。按照如下访问,

    clip_image014

            后台打印的结果为:clip_image016,age的值是一个列表集合,将POST提交方式age参数值跟GET提交方式age参数值合并啦,而且是GET在前,POST再后。而get_arguments获得最后一个。

    7.5 get_query_argument、get_query_arguments方法

           与get_argument、get_arguments功能类似,只是他仅仅是从URL查询参数中获取值。

    7.6  get_body_argument、get_body_arguments方法

          与get_argument、get_arguments功能类似,只是他仅仅是从body中获取值。

       再提一个问题?????get_query_argument、get _argument、get_body_argument的区别????测试一下便知。

       按如下修改post方法,

    def post(self, *args, **kwargs):
        print(self.request.query_arguments)
        print(self.request.arguments)
        print(self.request.body_arguments)
        print("POS age:" + self.get_argument("age"))
        self.write("POS age:" + self.get_argument("age"))

            按如下方式模拟访问:

    clip_image018

               后台打印的结果如下图:

    clip_image020

               所以得出结论就是,get _argument获取的值范围是get_query_argument与get_body_argument两者范围的合并。

    输出响应函数:

    7.7 clear方法

          重置响应的所有的头部以及内容。

    """Resets all headers and content for this response."""
    self._headers = httputil.HTTPHeaders({
        "Server": "TornadoServer/%s" % tornado.version,
        "Content-Type": "text/html; charset=UTF-8",
        "Date": httputil.format_timestamp(time.time()),
    })
    self.set_default_headers()
    self._write_buffer = []
    self._status_code = 200
    self._reason = httputil.responses[200]

          处理过程

    1. 首先默认设置3个响应头部,分别是Server,Content_Type、Date.
    2. 调用set_default_headers 方法,实现自定义header的设置。
    3. 其他参数的默认设置。

    7.9 set_default_headers方法

          上面说了set_default_headers 方法是实现请求响应头部的自定义实现,被子类重写。举个例子。

           在上面的BookRequestHandler中加入以下代码

    def set_default_headers(self):
        self._headers.add("custom_header1", "custom_header1")
        self._headers.add("custom_header2", "custom_header2")

           通过游览器访问http://localhost:8888/book,用firebug查询响应头。结果如下:

    clip_image022

    7.10 write方法

            将给定的块输出到输出缓存中。如果给定的块是一个字典,就会将这个快当成是JSON并且将Content_Type设置成application/json返回给客户端。但是,如果你想用不同的Content_Type发送JSON,可以在调用write方法后再调用set_header方法进行设置。

           注意,list 列表集合是不会转化成JSON的,原因是考虑到跨域的安全。所有的JSON输出都必须用字典包装。具体源码说明如下:

    clip_image024

    7.11 rend 方法

            用给定的参数渲染模板。这个涉及到模板的概念,后续再学。

    7.12  write_error方法

            重写自定义错误页面的实现。

            如果error是由没有捕获的异常(包括HTTPError)引起的,通过kwargs[|”exc_info”]能获取exc_info元组。实现代码如下:

    clip_image026

                举个例子实现一个自定义的错误页面,在BookRequestHandler中添加如下代码:

    def write_error(self, status_code, **kwargs):
        self.write("oh,my god!出错啦!!!!请联系系统管理员。
    ")
        self.write("呵呵,也没关系,我已经讲错误信息记录在日志中去了,系统管理员会看到的。
    ")
        if "exc_info" in kwargs:
            self.write("错误信息为:
    ")
            for line in traceback.format_exception(*kwargs["exc_info"]):
                self.write(line)
        self.finish()

    并将get方法修改成:

    def get(self, *args, **kwargs):
        # print(self.welcome + "
    " + "and value2 =" + self.value2)
        print("get method: Processing get Method...........")
        # self.write(self.welcome + "
    " + "and value2 =" + self.value2)
    
        # print(self.request.query_arguments)
        # print(self.request.arguments)
        # print(self.request.body_arguments)
        # self.write("参数name的值为:" + self.get_argument("name", "liaofei"))
    
        print(1/0)
       

                 print(1/0)语句,人为地制造一个错误。在游览器中访问http://localhost:8888/book,得到如下结果,

    clip_image028

                就是使用了重写write_error中的方法实现。

    Cookie相关函数

    7.13 get_cookie、set_cookie

          获取与设置cookie的值。这个对熟悉web开发的人,一看就明白。就不多解释。

    7.14 get_secure_cookie、set_secure_cookie

            获取与设置安全cookie的值。与7.13 相比加了一个单词secure(安全),就说明是为cookie安全加密解密的作用。这个方法涉及到cookie_secret 的Application 设置选项。来举个列子说明他们之间的区别:

    加入以下代码:

    class CookieRequestHandler(RequestHandler):
        def get(self, flag):
            if flag == '0':
                self.set_cookie("user", "liaofei")
            elif flag == '1':
                userCookie = self.get_cookie("user")
                self.write(userCookie)

          修改application的配置

    settings = {
        "debug":False,
        "cookie_secret":"gagagaarwrslijwlrjoiajfoajgojowurwojrowjojgfoaguuuu9",
    }
    
    app = Application(handlers=[
        (r"/book", BookRequestHandler, dict(welcome=welcome, value2=value2)),
        (r"/cookie/([0-1]+)", CookieRequestHandler),
    ], **settings)
        (r"/cookie/([0-1]+)", CookieRequestHandler),
    ], **settings)
         

            注意settings中设置了cookie_secret的值。

           访问http://localhost:8888/cookie/0时,调用set_cookie设置cookie中user的值为liaofei,并调用set_secure_cookie设置cookie 中usersecure的值同样为liaofei。用firebug查看这个相同值得cookie(都是liaofei),发现在游览器客户端的值是不一样的,一个加密过,一个未加密。具体结果如下图:

    clip_image030

  • 相关阅读:
    创意吃鱼法
    雅礼上课
    坏掉的项链Broken Necklace
    2018-04-02练习
    【11月12日】Hadoop架构
    【11月10日】Redis 主从复制技术
    【11月10日】Redis 缓存系统常见问题及解决方案
    【11月9日】Redis 持久化
    【11月7日】Redis核心对象和数据类型
    【11月7日】Redis简介
  • 原文地址:https://www.cnblogs.com/liaofeifight/p/4938987.html
Copyright © 2011-2022 走看看