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 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)