tornado: web.py 之 RequestHandler
RequestHandler这个类有超过1000行,看似十一个庞然大物,其实大多是注释和空方法,总体读起来非常容易。
这个类也是blog项目中所有handler的父类,想必大多数tornado项目也是如此,当然顾名思义这个类以及它的派生类用来处理tornado web server收到的httprequest,目前为止还没有看到httpserver的代码,无责任随便猜测一下,一个httprequest处理的大概流程:
- Httpserver收到请求
- 扔给Application实例去处理(调用__call__)
- Application的实例根据初始化时(或者后来通过add_handler添加的)路由表,生成相应的handler实例,把request扔进去进行处理。
目前默认tornado支持如下几种方法:
SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT", "OPTIONS")
貌似比几年前看得REST要多好几个啊。。。。head,patch,options是什么情况?
这个类因为是基础类,所以所以定义了一堆空方法,有几个方法值得拿出来单独列在这里的(虽然在基类中并没有任何实现)
def prepare(self): """Called at the beginning of a request before `get`/`post`/etc. Override this method to perform common initialization regardless of the request method. """ pass def on_finish(self): """Called after the end of a request. Override this method to perform cleanup, logging, etc. This method is a counterpart to `prepare`. ``on_finish`` may not produce any output, as it is called after the response has been sent to the client. """ pass def on_connection_close(self): """Called in async handlers if the client closed the connection. Override this to clean up resources associated with long-lived connections. Note that this method is called only if the connection was closed during asynchronous processing; if you need to do cleanup after every request override `on_finish` instead. Proxies may keep a connection open for a time (perhaps indefinitely) after the client has gone away, so this method may not be called promptly after the end user closes their connection. """ pass
基本上提供了某种程度上的callback,俺没有任何tornado经验,不知到有多少项目中真正用到了。
比较诡异的是,这个类里面定义了一些看起来重复的变量,比如下面两个
# UIModules are available as both `modules` and `_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 `_modules` to avoid # possible conflicts. self.ui["_modules"] = ObjectDict((n, self._ui_module(n, m)) for n, m in application.ui_modules.iteritems()) self.ui["modules"] = self.ui["_modules"]
还有这两个方法中用到的两个成员变量。。。
def set_header(self, name, value): """Sets the given response header name and value. If a datetime is given, we automatically format it according to the HTTP specification. If the value is not a string, we convert it to a string. All header values are then encoded as UTF-8. """ self._headers[name] = self._convert_header_value(value) def add_header(self, name, value): """Adds the given response header and value. Unlike `set_header`, `add_header` may be called multiple times to return multiple values for the same header. """ self._list_headers.append((name, self._convert_header_value(value)))
尼嘛意义何在啊啊。。。还没看到后面,暂时还理解不了。
然后下面是一些已经实现的方法,其中比较重要的一个个拿出来说说
- set_cookie
在handle中初始化一个名为_new_cookie的SimpleCookie实例,然后根据传入的参数设置domain, expires, path, max-age, 以及其他用户自定义的cookie pair - clear_cookie
这个方法挺有意思,clear的意思并不是“clear”,而是通过设置cookie的expire time让客户端浏览器中把cookie删除,而不是server端(本来清除cookie就是应该这么干吧?) - set_secure_cookie
这个方法会用Application.Settings中设置的secret来产生经过加密的cookie - get_secure_cookie
这是上面方法的配对方法 - redirect
设置response header中的location,并设置http status为3xx,这样的response一旦返回,浏览器默认会触发重定向到location中指定的url (看成中指的请自觉面壁) - write
这个方法的注释说的很清楚了。。。"""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/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx """
- render
这是个很重要函数,用来根据指定的模板生成最终的渲染页面。因为太长了,所以我跳过去了,以后回头看。 - render_string
这也是一个很重要的函数,不太长,但是看了一下。。。没看懂。。。主要是没明白template神码的是怎么回事。。其中一个term叫做template的namespace,我怎么看怎么不应该叫namespace,似乎叫context更加合适。。。。不信你看。。。namespace = dict( handler=self, request=self.request, current_user=self.current_user, locale=self.locale, =self.locale.translate, static_url=self.static_url, xsrf_form_html=self.xsrf_form_html, reverse_url=self.reverse_url) namespace.update(self.ui)
- flush
基本上这个方法会把buffer里面的东东弄出来调用注册的transform变成data chunk,生成header,最后调用httprequest的write方法写回到客户端 - finish
flush方法才真正的把数据写入request中(总觉的是不是用一个单独的response会比较不让人糊涂?),但是不会调用request的finish方法,而这些是在finish方法中调用的。所以一个request最后一个调用的一定是finish方法来结束请求。在finish方法内部也会调用flush,不过如果在finish之前调用过flush,行为可能是不确定的,大概会取决于传入flush方法的callback方法如何实现。但从代码来看,请不要在在代码中调用flush,这种粗重活,让finish来做好了。有一点需要注意的是,handler中某些方法的默认实现是会帮我们调用finish的,所以不用调用第二遍,否则会抛出exception,这些方法包括:
1. redirect
2. send_error
3. write_error - send_error
这个方法主要依赖write_error来产生错误页面返回用户 - write_error
看了一下代码,觉得自定义在handler中自定义一个get_error_html方法的做法比较靠谱 - locale
这个property在内部可能会调用两个不同的方法
1. self.get_user_locale
2. self.get_browser_locale
其中第一个方法默认没有提供实现,不过可以由程序员重载。提供两个方法的主要目的大概是:第一个方法不去理会浏览器中的language参数,而是从某个其他的地方,例如从数据库中读取用户设置的语言偏好(常用代理的同学应该体会比较深);第二个方法顾名思义,从用户request的header中读取Accept-language来决定用户的locale。 只有第一个方法调用失败或者返回None的情况下才会调用第二个方法。 - current_user
获取当前用户信息。默认提供的时候屁事没干,所以如果你需要用这个方法来做用户身份认证,获取用户信息的话,需要重载self.get_current_user方法。 - get_login_url
获得定义在Application.Settings中的get_login_url地址 - get_template_path
获得定义在Application.Settings中的get_template_path。这个路径应该是一个绝对路径,指向存放template文件的目录,或者返回None,用于从当前文件的相对路径开始查找。 - xsrf_token
property获得xsrf_token,用于防止forgery攻击。这个xsrf_token会同时保存在用户浏览器的cookie中,以及当前handler的_xsrf_token属性中,用于对接下来用户post消息的验证。不过这个实现我有点儿糊涂--如果用户cookie被盗怎么办?在设置用户cookie的时候至少用set_secure_cookie吧? - check_xsrf_cookie
检查参数中是否携带_xsrf, 或者header中包含X-Xsrftoken, X-Csrftoken。如果存在,再和handle中保存的值比较。 - xsrf_form_html
helper方法,生成一个hidden的form item代码片段,会随着form被post给server,也就是在check_xsrf_cookie中会检查的东东 - _execute
根据http类型分别调用handle中的get, post, delete, edit等方法
相关推荐
selectY 2020-07-18
zhangxuelong 2020-06-14
zhangxuelong 2020-06-14
牧码人 2020-06-14
hjhmpl 2020-06-14
thundor 2020-05-05
Cagey 2020-04-25
KarlDoenitz 2020-04-16
牧码人 2020-01-25
KarlDoenitz 2019-12-28
hjhmpl 2019-12-25
hjhmpl 2019-12-17
selectY 2019-12-11
KarlDoenitz 2019-12-06
selectY 2019-12-05
Cagey 2019-12-05
hjhmpl 2019-11-03
牧码人 2019-11-03
chenzhanhai 2019-04-09