1. 程式人生 > >python---redis緩存頁面前戲之剖析render源碼

python---redis緩存頁面前戲之剖析render源碼

http addition war ads include 參數 sca calling face

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\n + html[sloc:] if js_embed: js = self.render_embed_js(js_embed) sloc = html.rindex(b</body>) html = html[:sloc] + js + b\n + html[sloc:] if css_files: css = self.render_linked_css(css_files) hloc = html.index(b</head>) html = html[:hloc] + utf8(css) + b\n + html[hloc:] if css_embed: css = self.render_embed_css(css_embed) hloc = html.index(b</head>) html = html[:hloc] + css + b\n + html[hloc:] if html_heads: hloc = html.index(b</head>) html = html[:hloc] + b‘‘.join(html_heads) + b\n + html[hloc:] if html_bodies: hloc = html.index(b</body>) html = html[:hloc] + b‘‘.join(html_bodies) + b\n + 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 flushs 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)

python---redis緩存頁面前戲之剖析render源碼