• python---redis缓存页面前戏之剖析render源码


    1.初始代码:

    def get(self, *args, **kwargs):
        import time
        tm = time.time()
        self.render('home/index.html', time=tm)

    2.代码追踪render:

        # 第一次执行get方法时,通过render方法已经生成了数据,具体存在地方,参见源码
        def render(self, template_name, **kwargs):
            """Renders the template with the given arguments as the response."""
            if self._finished:
                raise RuntimeError("Cannot render() after finish()")
            html = self.render_string(template_name, **kwargs)
    
            # Insert the additional JS and CSS added by the modules on the page
            js_embed = []
            js_files = []
            css_embed = []
            css_files = []
            html_heads = []
            html_bodies = []
            for module in getattr(self, "_active_modules", {}).values():
                embed_part = module.embedded_javascript()
                if embed_part:
                    js_embed.append(utf8(embed_part))
                file_part = module.javascript_files()
                if file_part:
                    if isinstance(file_part, (unicode_type, bytes)):
                        js_files.append(file_part)
                    else:
                        js_files.extend(file_part)
                embed_part = module.embedded_css()
                if embed_part:
                    css_embed.append(utf8(embed_part))
                file_part = module.css_files()
                if file_part:
                    if isinstance(file_part, (unicode_type, bytes)):
                        css_files.append(file_part)
                    else:
                        css_files.extend(file_part)
                head_part = module.html_head()
                if head_part:
                    html_heads.append(utf8(head_part))
                body_part = module.html_body()
                if body_part:
                    html_bodies.append(utf8(body_part))
    
            if js_files:
                # Maintain order of JavaScript files given by modules
                js = self.render_linked_js(js_files)
                sloc = html.rindex(b'</body>')
                html = html[:sloc] + utf8(js) + b'
    ' + html[sloc:]
            if js_embed:
                js = self.render_embed_js(js_embed)
                sloc = html.rindex(b'</body>')
                html = html[:sloc] + js + b'
    ' + html[sloc:]
            if css_files:
                css = self.render_linked_css(css_files)
                hloc = html.index(b'</head>')
                html = html[:hloc] + utf8(css) + b'
    ' + html[hloc:]
            if css_embed:
                css = self.render_embed_css(css_embed)
                hloc = html.index(b'</head>')
                html = html[:hloc] + css + b'
    ' + html[hloc:]
            if html_heads:
                hloc = html.index(b'</head>')
                html = html[:hloc] + b''.join(html_heads) + b'
    ' + html[hloc:]
            if html_bodies:
                hloc = html.index(b'</body>')
                html = html[:hloc] + b''.join(html_bodies) + b'
    ' + html[hloc:]
            self.finish(html)

    数据先是保存在html局部变量中,传入finish中作为参数处理

    3.追踪finish代码:

        def finish(self, chunk=None):
            """Finishes this response, ending the HTTP request."""
            if self._finished:
                raise RuntimeError("finish() called twice")
    
            if chunk is not None:
                self.write(chunk)
    
            # Automatically support ETags and add the Content-Length header if
            # we have not flushed any content yet.
            if not self._headers_written:
                if (self._status_code == 200 and
                        self.request.method in ("GET", "HEAD") and
                        "Etag" not in self._headers):
                    self.set_etag_header()
                    if self.check_etag_header():
                        self._write_buffer = []
                        self.set_status(304)
                if (self._status_code in (204, 304) or
                        (self._status_code >= 100 and self._status_code < 200)):
                    assert not self._write_buffer, "Cannot send body with %s" % self._status_code
                    self._clear_headers_for_304()
                elif "Content-Length" not in self._headers:
                    content_length = sum(len(part) for part in self._write_buffer)
                    self.set_header("Content-Length", content_length)
    
            if hasattr(self.request, "connection"):
                # Now that the request is finished, clear the callback we
                # set on the HTTPConnection (which would otherwise prevent the
                # garbage collection of the RequestHandler when there
                # are keepalive connections)
                self.request.connection.set_close_callback(None)
    
            self.flush(include_footers=True)
            self.request.finish()
            self._log()
            self._finished = True
            self.on_finish()
            self._break_cycles()

    数据chunk(即是传入的html变量)在write方法中又进行处理

    4.追踪write方法:

        def write(self, chunk):
            """Writes the given chunk to the output buffer.
    
            To write the output to the network, use the flush() method below.
    
            If the given chunk is a dictionary, we write it as JSON and set
            the Content-Type of the response to be ``application/json``.
            (if you want to send JSON as a different ``Content-Type``, call
            set_header *after* calling write()).
    
            Note that lists are not converted to JSON because of a potential
            cross-site security vulnerability.  All JSON output should be
            wrapped in a dictionary.  More details at
            http://haacked.com/archive/2009/06/25/json-hijacking.aspx/ and
            https://github.com/facebook/tornado/issues/1009
            """
            if self._finished:
                raise RuntimeError("Cannot write() after finish()")
            if not isinstance(chunk, (bytes, unicode_type, dict)):
                message = "write() only accepts bytes, unicode, and dict objects"
                if isinstance(chunk, list):
                    message += ". Lists not accepted for security reasons; see " + 
                               "http://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.write"
                raise TypeError(message)
            if isinstance(chunk, dict):
                chunk = escape.json_encode(chunk)
                self.set_header("Content-Type", "application/json; charset=UTF-8")
            chunk = utf8(chunk)
            self._write_buffer.append(chunk)

    发现数据存放在类成员变量中_write_buffer

    若是想在自定义函数中直接输出该数据,为空

    def get(self, *args, **kwargs):
        import time
        tm = time.time()
        self.render('home/index.html', time=tm)
        print(self._write_buffer, len(self._write_buffer))  # 为空

    因为在finish方法中蓝色部分函数flush中对于数据进行了下一步处理:

    5.追踪flush方法(部分源码):

        但是在执行finish时,先要执行flush(部分源码)
        def flush(self, include_footers=False, callback=None):
            """Flushes the current output buffer to the network.
    
            The ``callback`` argument, if given, can be used for flow control:
            it will be run when all flushed data has been written to the socket.
            Note that only one flush callback can be outstanding at a time;
            if another flush occurs before the previous flush's callback
            has been run, the previous callback will be discarded.
    
            .. versionchanged:: 4.0
               Now returns a `.Future` if no callback is given.
            """
            chunk = b"".join(self._write_buffer)
            self._write_buffer = []   #将列表置为空

    所以要想获取数据:需要进行其他处理,例如:

    (1)修改源码,保存数据

    (2)重写父类函数flush,将数据提前截取保存在新的变量中

    为了尽量不修改源码,可以选择第二种方法

    6.重写父类函数flush

    class IndexHandler(tornado.web.RequestHandler):
        def flush(self, include_footers=False, callback=None):
            self._data_html = self._write_buffer
            super(IndexHandler,self).flush(include_footers,callback)

    将数据保存在自己定义的数据_data_html 中

    使用时可以直接获取数据

        def get(self, *args, **kwargs):
            import time
            tm = time.time()
            self.render('home/index.html',time=tm)
            print(self._data_html)
  • 相关阅读:
    HTML页引用CSS
    C#反射
    Marshal.SecureStringToBSTR
    SQL语句创建表和数据库
    抽象类和抽象方法
    3 Sum Closest
    Chapter 2: Binary Search & Sorted Array
    Spiral Matrix
    Pascal's Triangle
    Plus One
  • 原文地址:https://www.cnblogs.com/ssyfj/p/8608526.html
Copyright © 2020-2023  润新知