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. 对外部属性跟内部属性(带下划线)的赋值。注意:从源码可以看出application为RequestHandler类实例对象的属性
    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

    </span><span style="color: #0000ff">def</span> get(self, *args, **<span style="color: #000000">kwargs):
        </span><span style="color: #0000ff">print</span><span style="color: #000000">(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
    
    </span><span style="color: #0000ff">def</span> get(self, *args, **<span style="color: #000000">kwargs):
        </span><span style="color: #008000">#</span><span style="color: #008000">print(self.welcome + "
    " + "and value2 =" + self.value2)</span>
        <span style="color: #0000ff">print</span>(<span style="color: #800000">"</span><span style="color: #800000">get method: Processing get Method...........</span><span style="color: #800000">"</span><span style="color: #000000">)
        self.write(self.welcome </span>+ <span style="color: #800000">"</span><span style="color: #800000">
    </span><span style="color: #800000">"</span> + <span style="color: #800000">"</span><span style="color: #800000">and value2 =</span><span style="color: #800000">"</span> +<span style="color: #000000"> self.value2)
    
    </span><span style="color: #0000ff">def</span><span style="color: #000000"> set_default_headers(self):
        self._headers.add(</span><span style="color: #800000">"</span><span style="color: #800000">custom_header1</span><span style="color: #800000">"</span>, <span style="color: #800000">"</span><span style="color: #800000">custom_header1</span><span style="color: #800000">"</span><span style="color: #000000">)
        self._headers.add(</span><span style="color: #800000">"</span><span style="color: #800000">custom_header2</span><span style="color: #800000">"</span>, <span style="color: #800000">"</span><span style="color: #800000">custom_header2</span><span style="color: #800000">"</span><span style="color: #000000">)
    
    </span><span style="color: #0000ff">def</span><span style="color: #000000"> prepare(self):
        </span><span style="color: #0000ff">print</span>(<span style="color: #800000">"</span><span style="color: #800000">prepare method:initilize some variables....</span><span style="color: #800000">"</span><span style="color: #000000">)
    
    </span><span style="color: #0000ff">def</span><span style="color: #000000"> on_finish(self):
        </span><span style="color: #0000ff">print</span>(<span style="color: #800000">"</span><span style="color: #800000">on_finish method:clear some memory or close database connection</span><span style="color: #800000">"</span>)</pre>
    
    复制代码

          执行的结果如下:

    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)
    
    <span style="color: #008000">#</span><span style="color: #008000"> print(self.request.query_arguments)</span>
    <span style="color: #008000">#</span><span style="color: #008000"> print(self.request.arguments)</span>
    <span style="color: #008000">#</span><span style="color: #008000"> print(self.request.body_arguments)</span>
    <span style="color: #008000">#</span><span style="color: #008000"> self.write("参数name的值为:" + self.get_argument("name", "liaofei"))</span>
    
    <span style="color: #0000ff">print</span>(1/0)</pre>
    
    复制代码
       

                 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

  • 相关阅读:
    docker 部署aps.net MVC到windows容器
    docker 搭建私有仓库 harbor
    解决关于:Oracle数据库 插入数据中文乱码 显示问号???
    ionic cordova build android error: commamd failed with exit code eacces
    cordova build android Command failed with exit code EACCES
    Xcode 10 iOS12 "A valid provisioning profile for this executable was not found
    使用remix发布部署 发币 智能合约
    区块链: 编译发布智能合约
    mac 下常用命令备忘录
    JQuery fullCalendar 时间差 排序获取距当前最近的时间。
  • 原文地址:https://www.cnblogs.com/silence-cc/p/10010024.html
Copyright © 2011-2022 走看看