简介

  tornado,是我学到的第一个web框架是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本。这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效利用非阻塞式服务器环境,这个 Web 框架还包含了一些相关的有用工具 和优化。

Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。(关于如何扩容 服务器,以处理数以千计的客户端的连接的问题,请参阅 C10K problem。)

下载与安装

  1. pip3 install tornado
  2.  
  3. 源码安装
  4. https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz

但是好多的时候并不尽如人意,比如环境,比如系统,比如我们不知道的一些原因或者东西莫名其妙报些错误,这时候你需要

一、最基本tornado使用

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6.  
  7. class MainHandler(tornado.web.RequestHandler):
  8. def get(self):
  9. self.write("Hello, world")
  10.  
  11. application = tornado.web.Application([
  12. (r"/index", MainHandler),
  13. ])
  14.  
  15. if __name__ == "__main__":
  16. application.listen(8888)
  17. tornado.ioloop.IOLoop.instance().start()

上述代码执行过程:

  1. 第一步:执行脚本,监听 8888 端口
  2. 第二步:浏览器客户端访问 /index --> http://127.0.0.1:8888/index
  3. 第三步:服务器接受请求,并交由对应的类处理该请求
  4. 第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法
  5. 第五步:方法返回值的字符串内容发送浏览器

异步非阻塞实例

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. #!/usr/bin/env python
  4. # -*- coding:utf-8 -*-
  5.  
  6. import tornado.ioloop
  7. import tornado.web
  8. from tornado import httpclient
  9. from tornado.web import asynchronous
  10. from tornado import gen
  11.  
  12. import uimodules as md
  13. import uimethods as mt
  14.  
  15. class MainHandler(tornado.web.RequestHandler):
  16. @asynchronous
  17. @gen.coroutine
  18. def get(self):
  19. print 'start get '
  20. http = httpclient.AsyncHTTPClient()
  21. http.fetch("http://127.0.0.1:8008/post/", self.callback)
  22. self.write('end')
  23.  
  24. def callback(self, response):
  25. print response.body
  26.  
  27. settings = {
  28. 'template_path': 'template',
  29. 'static_path': 'static',
  30. 'static_url_prefix': '/static/',
  31. 'ui_methods': mt,
  32. 'ui_modules': md,
  33. }
  34.  
  35. application = tornado.web.Application([
  36. (r"/index", MainHandler),
  37. ], **settings)
  38.  
  39. if __name__ == "__main__":
  40. application.listen(8009)
  41. tornado.ioloop.IOLoop.instance().start()

二、tornado的路由系统

  路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应 函数,Tornado中每个url对应的是一个类  

tornado支持原生路由

三、模板的引擎

Tornao中的模板语言和django中类似,模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。

Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用 {% 和 %} 包起来的 例如 {% if len(items) > 2 %}。表达语句是使用 {{ 和 }} 包起来的,例如 {{ items[0] }}

控制语句和对应的 Python 语句的格式基本完全相同。我们支持 ifforwhile 和 try,这些语句逻辑结束的位置需要用 {% end %} 做标记。还通过 extends 和 block 语句实现了模板继承。这些在 template 模块 的代码文档中有着详细的描述。

注:在使用模板前需要在setting中设置模板路径:"template_path" : "tpl",

1、基本的使用

  1. 复制代码
  2. #!/usr/bin/env python
  3. # -*- coding:utf-8 -*-
  4.  
  5. import tornado.ioloop
  6. import tornado.web
  7.  
  8. class MainHandler(tornado.web.RequestHandler):
  9. def get(self):
  10. self.render("index.html", list_info = [11,22,33])
  11.  
  12. application = tornado.web.Application([
  13. (r"/index", MainHandler),
  14. ])
  15.  
  16. if __name__ == "__main__":
  17. application.listen(8888)
  18. tornado.ioloop.IOLoop.instance().start()

app.py

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  5. <title>老男孩</title>
  6. <link href="{{static_url("css/common.css")}}" rel="stylesheet" />
  7. </head>
  8. <body>
  9.  
  10. <div>
  11. <ul>
  12. {% for item in list_info %}
  13. <li>{{item}}</li>
  14. {% end %}
  15. </ul>
  16. </div>
  17.  
  18. <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
  19.  
  20. </body>
  21. </html>

index.html

  1. 在模板中默认提供了一些函数、字段、类以供模板使用:
  2.  
  3. escape: tornado.escape.xhtml_escape 的別名
  4. xhtml_escape: tornado.escape.xhtml_escape 的別名
  5. url_escape: tornado.escape.url_escape 的別名
  6. json_encode: tornado.escape.json_encode 的別名
  7. squeeze: tornado.escape.squeeze 的別名
  8. linkify: tornado.escape.linkify 的別名
  9. datetime: Python datetime 模组
  10. handler: 当前的 RequestHandler 对象
  11. request: handler.request 的別名
  12. current_user: handler.current_user 的別名
  13. locale: handler.locale 的別名
  14. _: handler.locale.translate 的別名
  15. static_url: for handler.static_url 的別名
  16. xsrf_form_html: handler.xsrf_form_html 的別名

其他方法

2、母版

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  5. <title>老男孩</title>
  6. <link href="{{static_url("css/common.css")}}" rel="stylesheet" />
  7. {% block CSS %}{% end %}
  8. </head>
  9. <body>
  10.  
  11. <div class="pg-header">
  12.  
  13. </div>
  14.  
  15. {% block RenderBody %}{% end %}
  16.  
  17. <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
  18.  
  19. {% block JavaScript %}{% end %}
  20. </body>
  21. </html>

layout

  1. {% extends 'layout.html'%}
  2. {% block CSS %}
  3. <link href="{{static_url("css/index.css")}}" rel="stylesheet" />
  4. {% end %}
  5.  
  6. {% block RenderBody %}
  7. <h1>Index</h1>
  8.  
  9. <ul>
  10. {% for item in li %}
  11. <li>{{item}}</li>
  12. {% end %}
  13. </ul>
  14.  
  15. {% end %}
  16.  
  17. {% block JavaScript %}
  18.  
  19. {% end %}

index.html

3、导入

  1. <div>
  2. <ul>
  3. <li>1024</li>
  4. <li>42区</li>
  5. </ul>
  6. </div>

heaher.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  5. <title>老男孩</title>
  6. <link href="{{static_url("css/common.css")}}" rel="stylesheet" />
  7. </head>
  8. <body>
  9.  
  10. <div class="pg-header">
  11. {% include 'header.html' %}
  12. </div>
  13.  
  14. <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
  15.  
  16. </body>
  17. </html>

index.html

4、自定义UIMethod以UIModule

定义

  1. # uimethods.py
  2.  
  3. def tab(self):
  4. return 'UIMethod'

uimmethods.py

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. from tornado.web import UIModule
  4. from tornado import escape
  5.  
  6. class custom(UIModule):
  7.  
  8. def render(self, *args, **kwargs):
  9. return escape.xhtml_escape('<h1>wupeiqi</h1>')
  10. #return escape.xhtml_escape('<h1>wupeiqi</h1>')

uimodules.py

注册

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. #!/usr/bin/env python
  4. # -*- coding:utf-8 -*-
  5.  
  6. import tornado.ioloop
  7. import tornado.web
  8. from tornado.escape import linkify
  9. import uimodules as md
  10. import uimethods as mt
  11.  
  12. class MainHandler(tornado.web.RequestHandler):
  13. def get(self):
  14. self.render('index.html')
  15.  
  16. settings = {
  17. 'template_path': 'template',
  18. 'static_path': 'static',
  19. 'static_url_prefix': '/static/',
  20. 'ui_methods': mt,
  21. 'ui_modules': md,
  22. }
  23.  
  24. application = tornado.web.Application([
  25. (r"/index", MainHandler),
  26. ], **settings)
  27.  
  28. if __name__ == "__main__":
  29. application.listen(8009)
  30. tornado.ioloop.IOLoop.instance().start()

使用

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <link href="{{static_url("commons.css")}}" rel="stylesheet" />
  7. </head>
  8. <body>
  9. <h1>hello</h1>
  10. {% module custom(123) %}
  11. {{ tab() }}
  12. </body>

四、静态文件

对于静态文件,可以配置静态文件的目录和前段使用时的前缀,并且Tornaodo还支持静态文件缓存。

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6.  
  7. class MainHandler(tornado.web.RequestHandler):
  8. def get(self):
  9. self.render('home/index.html')
  10.  
  11. settings = {
  12. 'template_path': 'template',
  13. 'static_path': 'static',
  14. 'static_url_prefix': '/static/',
  15. }
  16.  
  17. application = tornado.web.Application([
  18. (r"/index", MainHandler),
  19. ], **settings)
  20.  
  21. if __name__ == "__main__":
  22. application.listen(80)
  23. tornado.ioloop.IOLoop.instance().start()

app.py

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <link href="{{static_url("commons.css")}}" rel="stylesheet" />
  7. </head>
  8. <body>
  9. <h1>hello</h1>
  10. </body>
  11. </html>

index.html

静态文件缓存的实现

  1. def get_content_version(cls, abspath):
  2. """Returns a version string for the resource at the given path.
  3.  
  4. This class method may be overridden by subclasses. The
  5. default implementation is a hash of the file's contents.
  6.  
  7. .. versionadded:: 3.1
  8. """
  9. data = cls.get_content(abspath)
  10. hasher = hashlib.md5()
  11. if isinstance(data, bytes):
  12. hasher.update(data)
  13. else:
  14. for chunk in data:
  15. hasher.update(chunk)
  16. return hasher.hexdigest()

五、cookie

Tornado中可以对cookie进行操作,并且还可以对cookie进行签名以放置伪造。

1、基本操作

  1. class MainHandler(tornado.web.RequestHandler):
  2. def get(self):
  3. if not self.get_cookie("mycookie"):
  4. self.set_cookie("mycookie", "myvalue")
  5. self.write("Your cookie was not set yet!")
  6. else:
  7. self.write("Your cookie was set!")

2、加密cookie(签名)

Cookie 很容易被恶意的客户端伪造。加入你想在 cookie 中保存当前登陆用户的 id 之类的信息,你需要对 cookie 作签名以防止伪造。Tornado 通过 set_secure_cookie 和 get_secure_cookie 方法直接支持了这种功能。 要使用这些方法,你需要在创建应用时提供一个密钥,名字为 cookie_secret。 你可以把它作为一个关键词参数传入应用的设置中:

  1. class MainHandler(tornado.web.RequestHandler):
  2. def get(self):
  3. if not self.get_secure_cookie("mycookie"):
  4. self.set_secure_cookie("mycookie", "myvalue")
  5. self.write("Your cookie was not set yet!")
  6. else:
  7. self.write("Your cookie was set!")
  8.  
  9. application = tornado.web.Application([
  10. (r"/", MainHandler),
  11. ], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")

内部算法

  1. def _create_signature_v1(secret, *parts):
  2. hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
  3. for part in parts:
  4. hash.update(utf8(part))
  5. return utf8(hash.hexdigest())
  6.  
  7. # 加密
  8. def _create_signature_v2(secret, s):
  9. hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
  10. hash.update(utf8(s))
  11. return utf8(hash.hexdigest())
  12.  
  13. def create_signed_value(secret, name, value, version=None, clock=None,
  14. key_version=None):
  15. if version is None:
  16. version = DEFAULT_SIGNED_VALUE_VERSION
  17. if clock is None:
  18. clock = time.time
  19.  
  20. timestamp = utf8(str(int(clock())))
  21. value = base64.b64encode(utf8(value))
  22. if version == 1:
  23. signature = _create_signature_v1(secret, name, value, timestamp)
  24. value = b"|".join([value, timestamp, signature])
  25. return value
  26. elif version == 2:
  27. # The v2 format consists of a version number and a series of
  28. # length-prefixed fields "%d:%s", the last of which is a
  29. # signature, all separated by pipes. All numbers are in
  30. # decimal format with no leading zeros. The signature is an
  31. # HMAC-SHA256 of the whole string up to that point, including
  32. # the final pipe.
  33. #
  34. # The fields are:
  35. # - format version (i.e. 2; no length prefix)
  36. # - key version (integer, default is 0)
  37. # - timestamp (integer seconds since epoch)
  38. # - name (not encoded; assumed to be ~alphanumeric)
  39. # - value (base64-encoded)
  40. # - signature (hex-encoded; no length prefix)
  41. def format_field(s):
  42. return utf8("%d:" % len(s)) + utf8(s)
  43. to_sign = b"|".join([
  44. b"",
  45. format_field(str(key_version or 0)),
  46. format_field(timestamp),
  47. format_field(name),
  48. format_field(value),
  49. b''])
  50.  
  51. if isinstance(secret, dict):
  52. assert key_version is not None, 'Key version must be set when sign key dict is used'
  53. assert version >= 2, 'Version must be at least 2 for key version support'
  54. secret = secret[key_version]
  55.  
  56. signature = _create_signature_v2(secret, to_sign)
  57. return to_sign + signature
  58. else:
  59. raise ValueError("Unsupported version %d" % version)
  60.  
  61. # 解密
  62. def _decode_signed_value_v1(secret, name, value, max_age_days, clock):
  63. parts = utf8(value).split(b"|")
  64. if len(parts) != 3:
  65. return None
  66. signature = _create_signature_v1(secret, name, parts[0], parts[1])
  67. if not _time_independent_equals(parts[2], signature):
  68. gen_log.warning("Invalid cookie signature %r", value)
  69. return None
  70. timestamp = int(parts[1])
  71. if timestamp < clock() - max_age_days * 86400:
  72. gen_log.warning("Expired cookie %r", value)
  73. return None
  74. if timestamp > clock() + 31 * 86400:
  75. # _cookie_signature does not hash a delimiter between the
  76. # parts of the cookie, so an attacker could transfer trailing
  77. # digits from the payload to the timestamp without altering the
  78. # signature. For backwards compatibility, sanity-check timestamp
  79. # here instead of modifying _cookie_signature.
  80. gen_log.warning("Cookie timestamp in future; possible tampering %r",
  81. value)
  82. return None
  83. if parts[1].startswith(b""):
  84. gen_log.warning("Tampered cookie %r", value)
  85. return None
  86. try:
  87. return base64.b64decode(parts[0])
  88. except Exception:
  89. return None
  90.  
  91. def _decode_fields_v2(value):
  92. def _consume_field(s):
  93. length, _, rest = s.partition(b':')
  94. n = int(length)
  95. field_value = rest[:n]
  96. # In python 3, indexing bytes returns small integers; we must
  97. # use a slice to get a byte string as in python 2.
  98. if rest[n:n + 1] != b'|':
  99. raise ValueError("malformed v2 signed value field")
  100. rest = rest[n + 1:]
  101. return field_value, rest
  102.  
  103. rest = value[2:] # remove version number
  104. key_version, rest = _consume_field(rest)
  105. timestamp, rest = _consume_field(rest)
  106. name_field, rest = _consume_field(rest)
  107. value_field, passed_sig = _consume_field(rest)
  108. return int(key_version), timestamp, name_field, value_field, passed_sig
  109.  
  110. def _decode_signed_value_v2(secret, name, value, max_age_days, clock):
  111. try:
  112. key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value)
  113. except ValueError:
  114. return None
  115. signed_string = value[:-len(passed_sig)]
  116.  
  117. if isinstance(secret, dict):
  118. try:
  119. secret = secret[key_version]
  120. except KeyError:
  121. return None
  122.  
  123. expected_sig = _create_signature_v2(secret, signed_string)
  124. if not _time_independent_equals(passed_sig, expected_sig):
  125. return None
  126. if name_field != utf8(name):
  127. return None
  128. timestamp = int(timestamp)
  129. if timestamp < clock() - max_age_days * 86400:
  130. # The signature has expired.
  131. return None
  132. try:
  133. return base64.b64decode(value_field)
  134. except Exception:
  135. return None
  136.  
  137. def get_signature_key_version(value):
  138. value = utf8(value)
  139. version = _get_version(value)
  140. if version < 2:
  141. return None
  142. try:
  143. key_version, _, _, _, _ = _decode_fields_v2(value)
  144. except ValueError:
  145. return None
  146.  
  147. return key_version

签名Cookie的本质是:

  1. cookie过程:
  2.  
  3. 将值进行base64加密
  4. 对除值以外的内容进行签名,哈希算法(无法逆向解析)
  5. 拼接 签名 + 加密值
  6. cookie过程:
  7.  
  8. 读取 签名 + 加密值
  9. 对签名进行验证
  10. base64解密,获取值内容
  11. 注:许多API验证机制和安全cookie的实现机制相同。
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6.  
  7. class MainHandler(tornado.web.RequestHandler):
  8.  
  9. def get(self):
  10. login_user = self.get_secure_cookie("login_user", None)
  11. if login_user:
  12. self.write(login_user)
  13. else:
  14. self.redirect('/login')
  15.  
  16. class LoginHandler(tornado.web.RequestHandler):
  17. def get(self):
  18. self.current_user()
  19.  
  20. self.render('login.html', **{'status': ''})
  21.  
  22. def post(self, *args, **kwargs):
  23.  
  24. username = self.get_argument('name')
  25. password = self.get_argument('pwd')
  26. if username == 'wupeiqi' and password == '':
  27. self.set_secure_cookie('login_user', '武沛齐')
  28. self.redirect('/')
  29. else:
  30. self.render('login.html', **{'status': '用户名或密码错误'})
  31.  
  32. settings = {
  33. 'template_path': 'template',
  34. 'static_path': 'static',
  35. 'static_url_prefix': '/static/',
  36. 'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh'
  37. }
  38.  
  39. application = tornado.web.Application([
  40. (r"/index", MainHandler),
  41. (r"/login", LoginHandler),
  42. ], **settings)
  43.  
  44. if __name__ == "__main__":
  45. application.listen(8888)
  46. tornado.ioloop.IOLoop.instance().start()

基于cookie实现用户验证

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6.  
  7. class BaseHandler(tornado.web.RequestHandler):
  8.  
  9. def get_current_user(self):
  10. return self.get_secure_cookie("login_user")
  11.  
  12. class MainHandler(BaseHandler):
  13.  
  14. @tornado.web.authenticated
  15. def get(self):
  16. login_user = self.current_user
  17. self.write(login_user)
  18.  
  19. class LoginHandler(tornado.web.RequestHandler):
  20. def get(self):
  21. self.current_user()
  22.  
  23. self.render('login.html', **{'status': ''})
  24.  
  25. def post(self, *args, **kwargs):
  26.  
  27. username = self.get_argument('name')
  28. password = self.get_argument('pwd')
  29. if username == 'wupeiqi' and password == '':
  30. self.set_secure_cookie('login_user', '呵呵')
  31. self.redirect('/')
  32. else:
  33. self.render('login.html', **{'status': '用户名或密码错误'})
  34.  
  35. settings = {
  36. 'template_path': 'template',
  37. 'static_path': 'static',
  38. 'static_url_prefix': '/static/',
  39. 'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
  40. 'login_url': '/login'
  41. }
  42.  
  43. application = tornado.web.Application([
  44. (r"/index", MainHandler),
  45. (r"/login", LoginHandler),
  46. ], **settings)
  47.  
  48. if __name__ == "__main__":
  49. application.listen(8888)
  50. tornado.ioloop.IOLoop.instance().start()

基于签名的cookie实现用户验证

3、JavaScript操作Cookie

由于Cookie保存在浏览器端,所以在浏览器端也可以使用JavaScript来操作Cookie。

  1. /*
  2. 设置cookie,指定秒数过期
  3. */
  4. function setCookie(name,value,expires){
  5. var temp = [];
  6. var current_date = new Date();
  7. current_date.setSeconds(current_date.getSeconds() + 5);
  8. document.cookie = name + "= "+ value +";expires=" + current_date.toUTCString();
  9. }

对于参数:

  • domain   指定域名下的cookie
  • path       域名下指定url中的cookie
  • secure    https使用

注:jQuery中也有指定的插件 jQuery Cookie 专门用于操作cookie,猛击这里

六、csrf

Tornado中的夸张请求伪造和Django中的相似,跨站伪造请求(Cross-site request forgery)

  1. settings = {
  2. "xsrf_cookies": True,
  3. }
  4. application = tornado.web.Application([
  5. (r"/", MainHandler),
  6. (r"/login", LoginHandler),
  7. ], **settings)

配置

  1. <form action="/new_message" method="post">
  2. {{ xsrf_form_html() }}
  3. <input type="text" name="message"/>
  4. <input type="submit" value="Post"/>
  5. </form>

普通表单

  1. function getCookie(name) {
  2. var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
  3. return r ? r[1] : undefined;
  4. }
  5.  
  6. jQuery.postJSON = function(url, args, callback) {
  7. args._xsrf = getCookie("_xsrf");
  8. $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
  9. success: function(response) {
  10. callback(eval("(" + response + ")"));
  11. }});
  12. };

ajax

注:Ajax使用时,本质上就是去获取本地的cookie,携带cookie再来发送请求

七、上传文件

1、Form表单上传

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  5. <title>上传文件</title>
  6. </head>
  7. <body>
  8. <form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" >
  9. <input name="fff" id="my_file" type="file" />
  10. <input type="submit" value="提交" />
  11. </form>
  12. </body>
  13. </html>
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6.  
  7. class MainHandler(tornado.web.RequestHandler):
  8. def get(self):
  9.  
  10. self.render('index.html')
  11.  
  12. def post(self, *args, **kwargs):
  13. file_metas = self.request.files["fff"]
  14. # print(file_metas)
  15. for meta in file_metas:
  16. file_name = meta['filename']
  17. with open(file_name,'wb') as up:
  18. up.write(meta['body'])
  19.  
  20. settings = {
  21. 'template_path': 'template',
  22. }
  23.  
  24. application = tornado.web.Application([
  25. (r"/index", MainHandler),
  26. ], **settings)
  27.  
  28. if __name__ == "__main__":
  29. application.listen(8000)
  30. tornado.ioloop.IOLoop.instance().start()

app.py

ajax上传

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6.  
  7. class MainHandler(tornado.web.RequestHandler):
  8. def get(self):
  9.  
  10. self.render('index.html')
  11.  
  12. def post(self, *args, **kwargs):
  13. file_metas = self.request.files["fff"]
  14. # print(file_metas)
  15. for meta in file_metas:
  16. file_name = meta['filename']
  17. with open(file_name,'wb') as up:
  18. up.write(meta['body'])
  19.  
  20. settings = {
  21. 'template_path': 'template',
  22. }
  23.  
  24. application = tornado.web.Application([
  25. (r"/index", MainHandler),
  26. ], **settings)
  27.  
  28. if __name__ == "__main__":
  29. application.listen(8000)
  30. tornado.ioloop.IOLoop.instance().start()

HTML - XMLHttpRequest

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <input type="file" id="img" />
  9. <input type="button" onclick="UploadFile();" />
  10. <script>
  11. function UploadFile(){
  12. var fileObj = $("#img")[0].files[0];
  13. var form = new FormData();
  14. form.append("k1", "v1");
  15. form.append("fff", fileObj);
  16.  
  17. $.ajax({
  18. type:'POST',
  19. url: '/index',
  20. data: form,
  21. processData: false, // tell jQuery not to process the data
  22. contentType: false, // tell jQuery not to set contentType
  23. success: function(arg){
  24. console.log(arg);
  25. }
  26. })
  27. }
  28. </script>
  29. </body>
  30. </html>

ajax-jquery

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" >
  9. <div id="main">
  10. <input name="fff" id="my_file" type="file" />
  11. <input type="button" name="action" value="Upload" onclick="redirect()"/>
  12. <iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe>
  13. </div>
  14. </form>
  15.  
  16. <script>
  17. function redirect(){
  18. document.getElementById('my_iframe').onload = Testt;
  19. document.getElementById('my_form').target = 'my_iframe';
  20. document.getElementById('my_form').submit();
  21.  
  22. }
  23.  
  24. function Testt(ths){
  25. var t = $("#my_iframe").contents().find("body").text();
  26. console.log(t);
  27. }
  28. </script>
  29. </body>
  30. </html>

HTML - iframe

  1. import tornado.ioloop
  2. import tornado.web
  3.  
  4. class MainHandler(tornado.web.RequestHandler):
  5. def get(self):
  6.  
  7. self.render('index.html')
  8.  
  9. def post(self, *args, **kwargs):
  10. file_metas = self.request.files["fff"]
  11. # print(file_metas)
  12. for meta in file_metas:
  13. file_name = meta['filename']
  14. with open(file_name,'wb') as up:
  15. up.write(meta['body'])
  16.  
  17. settings = {
  18. 'template_path': 'template',
  19. }
  20.  
  21. application = tornado.web.Application([
  22. (r"/index", MainHandler),
  23. ], **settings)
  24.  
  25. if __name__ == "__main__":
  26. application.listen(8000)
  27. tornado.ioloop.IOLoop.instance().start()

python

  1. <script type="text/javascript">
  2.  
  3. $(document).ready(function () {
  4.  
  5. $("#formsubmit").click(function () {
  6.  
  7. var iframe = $('<iframe name="postiframe" id="postiframe" style="display: none"></iframe>');
  8.  
  9. $("body").append(iframe);
  10.  
  11. var form = $('#theuploadform');
  12. form.attr("action", "/upload.aspx");
  13. form.attr("method", "post");
  14.  
  15. form.attr("encoding", "multipart/form-data");
  16. form.attr("enctype", "multipart/form-data");
  17.  
  18. form.attr("target", "postiframe");
  19. form.attr("file", $('#userfile').val());
  20. form.submit();
  21.  
  22. $("#postiframe").load(function () {
  23. iframeContents = this.contentWindow.document.body.innerHTML;
  24. $("#textarea").html(iframeContents);
  25. });
  26.  
  27. return false;
  28.  
  29. });
  30.  
  31. });
  32.  
  33. </script>
  34.  
  35. <form id="theuploadform">
  36. <input id="userfile" name="userfile" size="" type="file" />
  37. <input id="formsubmit" type="submit" value="Send File" />
  38. </form>
  39.  
  40. <div id="textarea">
  41. </div>

扩展:基于iframe实现Ajax上传示例

八、验证码

验证码原理在于后台自动创建一张带有随机内容的图片,然后将内容通过img标签输出到页面。

安装图像处理模块

pip3 install pillow

验证码Demo源码下载:猛击这里

九、异步非阻塞

1、基本使用

装饰器 + Future 从而实现Tornado的异步非阻塞

  1. class AsyncHandler(tornado.web.RequestHandler):
  2.  
  3. @gen.coroutine
  4. def get(self):
  5. future = Future()
  6. future.add_done_callback(self.doing)
  7. yield future
  8. # 或
  9. # tornado.ioloop.IOLoop.current().add_future(future,self.doing)
  10. # yield future
  11.  
  12. def doing(self,*args, **kwargs):
  13. self.write('async')
  14. self.finish()

当发送GET请求时,由于方法被@gen.coroutine装饰且yield 一个 Future对象,那么Tornado会等待,等待用户向future对象中放置数据或者发送信号,如果获取到数据或信号之后,就开始执行doing方法。

异步非阻塞体现在当在Tornaod等待用户向future对象中放置数据时,还可以处理其他请求。

注意:在等待用户向future对象中放置数据或信号时,此连接是不断开的。

2、同步阻塞和异步非阻塞对比

  1. class SyncHandler(tornado.web.RequestHandler):
  2.  
  3. def get(self):
  4. self.doing()
  5. self.write('sync')
  6.  
  7. def doing(self):
  8. time.sleep(10)

同步阻塞

  1. class AsyncHandler(tornado.web.RequestHandler):
  2. @gen.coroutine
  3. def get(self):
  4. future = Future()
  5. tornado.ioloop.IOLoop.current().add_timeout(time.time() + 5, self.doing)
  6. yield future
  7.  
  8. def doing(self, *args, **kwargs):
  9. self.write('async')
  10. self.finish()

异步非阻塞

3、httpclient类库

Tornado提供了httpclient类库用于发送Http请求,其配合Tornado的异步非阻塞使用。

  1. class AsyncHandler(tornado.web.RequestHandler):
  2. @gen.coroutine
  3. def get(self):
  4. from tornado import httpclient
  5.  
  6. http = httpclient.AsyncHTTPClient()
  7. yield http.fetch("http://www.google.com", self.endding)
  8.  
  9. def endding(self, response):
  10. print(len(response.body))
  11. self.write('ok')
  12. self.finish()

十、session

1、面向对象基础

面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. class Foo(object):
  5.  
  6. def __getitem__(self, key):
  7. print '__getitem__',key
  8.  
  9. def __setitem__(self, key, value):
  10. print '__setitem__',key,value
  11.  
  12. def __delitem__(self, key):
  13. print '__delitem__',key
  14.  
  15. obj = Foo()
  16. result = obj['k1']
  17. #obj['k2'] = 'wupeiqi'
  18. #del obj['k1']

2、Tornado扩展

Tornado框架中,默认执行Handler的get/post等方法之前默认会执行 initialize方法,所以可以通过自定义的方式使得所有请求在处理前执行操作...

  1. class BaseHandler(tornado.web.RequestHandler):
  2.  
  3. def initialize(self):
  4. self.xxoo = "wupeiqi"
  5.  
  6. class MainHandler(BaseHandler):
  7.  
  8. def get(self):
  9. print(self.xxoo)
  10. self.write('index')
  11.  
  12. class IndexHandler(BaseHandler):
  13.  
  14. def get(self):
  15. print(self.xxoo)
  16. self.write('index')

3、session

session其实就是定义在服务器端用于保存用户回话的容器,其必须依赖cookie才能实现

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. import config
  4. from hashlib import sha1
  5. import os
  6. import time
  7.  
  8. create_session_id = lambda: sha1(bytes('%s%s' % (os.urandom(16), time.time()), encoding='utf-8')).hexdigest()
  9.  
  10. class SessionFactory:
  11.  
  12. @staticmethod
  13. def get_session_obj(handler):
  14. obj = None
  15.  
  16. if config.SESSION_TYPE == "cache":
  17. obj = CacheSession(handler)
  18. elif config.SESSION_TYPE == "memcached":
  19. obj = MemcachedSession(handler)
  20. elif config.SESSION_TYPE == "redis":
  21. obj = RedisSession(handler)
  22. return obj
  23.  
  24. class CacheSession:
  25. session_container = {}
  26. session_id = "__sessionId__"
  27.  
  28. def __init__(self, handler):
  29. self.handler = handler
  30. client_random_str = handler.get_cookie(CacheSession.session_id, None)
  31. if client_random_str and client_random_str in CacheSession.session_container:
  32. self.random_str = client_random_str
  33. else:
  34. self.random_str = create_session_id()
  35. CacheSession.session_container[self.random_str] = {}
  36.  
  37. expires_time = time.time() + config.SESSION_EXPIRES
  38. handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time)
  39.  
  40. def __getitem__(self, key):
  41. ret = CacheSession.session_container[self.random_str].get(key, None)
  42. return ret
  43.  
  44. def __setitem__(self, key, value):
  45. CacheSession.session_container[self.random_str][key] = value
  46.  
  47. def __delitem__(self, key):
  48. if key in CacheSession.session_container[self.random_str]:
  49. del CacheSession.session_container[self.random_str][key]
  50.  
  51. class RedisSession:
  52. def __init__(self, handler):
  53. pass
  54.  
  55. class MemcachedSession:
  56. def __init__(self, handler):
  57. pass

自定义session

4、分布式Session

  1. #!/usr/bin/env python
  2. #coding:utf-8
  3.  
  4. import sys
  5. import math
  6. from bisect import bisect
  7.  
  8. if sys.version_info >= (2, 5):
  9. import hashlib
  10. md5_constructor = hashlib.md5
  11. else:
  12. import md5
  13. md5_constructor = md5.new
  14.  
  15. class HashRing(object):
  16. """一致性哈希"""
  17.  
  18. def __init__(self,nodes):
  19. '''初始化
  20. nodes : 初始化的节点,其中包含节点已经节点对应的权重
  21. 默认每一个节点有32个虚拟节点
  22. 对于权重,通过多创建虚拟节点来实现
  23. 如:nodes = [
  24. {'host':'127.0.0.1:8000','weight':1},
  25. {'host':'127.0.0.1:8001','weight':2},
  26. {'host':'127.0.0.1:8002','weight':1},
  27. ]
  28. '''
  29.  
  30. self.ring = dict()
  31. self._sorted_keys = []
  32.  
  33. self.total_weight = 0
  34.  
  35. self.__generate_circle(nodes)
  36.  
  37. def __generate_circle(self,nodes):
  38. for node_info in nodes:
  39. self.total_weight += node_info.get('weight',1)
  40.  
  41. for node_info in nodes:
  42. weight = node_info.get('weight',1)
  43. node = node_info.get('host',None)
  44.  
  45. virtual_node_count = math.floor((32*len(nodes)*weight) / self.total_weight)
  46. for i in xrange(0,int(virtual_node_count)):
  47. key = self.gen_key_thirty_two( '%s-%s' % (node, i) )
  48. if self._sorted_keys.__contains__(key):
  49. raise Exception('该节点已经存在.')
  50. self.ring[key] = node
  51. self._sorted_keys.append(key)
  52.  
  53. def add_node(self,node):
  54. ''' 新建节点
  55. node : 要添加的节点,格式为:{'host':'127.0.0.1:8002','weight':1},其中第一个元素表示节点,第二个元素表示该节点的权重。
  56. '''
  57. node = node.get('host',None)
  58. if not node:
  59. raise Exception('节点的地址不能为空.')
  60.  
  61. weight = node.get('weight',1)
  62.  
  63. self.total_weight += weight
  64. nodes_count = len(self._sorted_keys) + 1
  65.  
  66. virtual_node_count = math.floor((32 * nodes_count * weight) / self.total_weight)
  67. for i in xrange(0,int(virtual_node_count)):
  68. key = self.gen_key_thirty_two( '%s-%s' % (node, i) )
  69. if self._sorted_keys.__contains__(key):
  70. raise Exception('该节点已经存在.')
  71. self.ring[key] = node
  72. self._sorted_keys.append(key)
  73.  
  74. def remove_node(self,node):
  75. ''' 移除节点
  76. node : 要移除的节点 '127.0.0.1:8000'
  77. '''
  78. for key,value in self.ring.items():
  79. if value == node:
  80. del self.ring[key]
  81. self._sorted_keys.remove(key)
  82.  
  83. def get_node(self,string_key):
  84. '''获取 string_key 所在的节点'''
  85. pos = self.get_node_pos(string_key)
  86. if pos is None:
  87. return None
  88. return self.ring[ self._sorted_keys[pos]].split(':')
  89.  
  90. def get_node_pos(self,string_key):
  91. '''获取 string_key 所在的节点的索引'''
  92. if not self.ring:
  93. return None
  94.  
  95. key = self.gen_key_thirty_two(string_key)
  96. nodes = self._sorted_keys
  97. pos = bisect(nodes, key)
  98. return pos
  99.  
  100. def gen_key_thirty_two(self, key):
  101.  
  102. m = md5_constructor()
  103. m.update(key)
  104. return long(m.hexdigest(), 16)
  105.  
  106. def gen_key_sixteen(self,key):
  107.  
  108. b_key = self.__hash_digest(key)
  109. return self.__hash_val(b_key, lambda x: x)
  110.  
  111. def __hash_val(self, b_key, entry_fn):
  112. return (( b_key[entry_fn(3)] << 24)|(b_key[entry_fn(2)] << 16)|(b_key[entry_fn(1)] << 8)| b_key[entry_fn(0)] )
  113.  
  114. def __hash_digest(self, key):
  115. m = md5_constructor()
  116. m.update(key)
  117. return map(ord, m.digest())
  118.  
  119. """
  120. nodes = [
  121. {'host':'127.0.0.1:8000','weight':1},
  122. {'host':'127.0.0.1:8001','weight':2},
  123. {'host':'127.0.0.1:8002','weight':1},
  124. ]
  125.  
  126. ring = HashRing(nodes)
  127. result = ring.get_node('98708798709870987098709879087')
  128. print result
  129.  
  130. """

一致性哈希

  1. from hashlib import sha1
  2. import os, time
  3.  
  4. create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()
  5.  
  6. class Session(object):
  7.  
  8. session_id = "__sessionId__"
  9.  
  10. def __init__(self, request):
  11. session_value = request.get_cookie(Session.session_id)
  12. if not session_value:
  13. self._id = create_session_id()
  14. else:
  15. self._id = session_value
  16. request.set_cookie(Session.session_id, self._id)
  17.  
  18. def __getitem__(self, key):
  19. # 根据 self._id ,在一致性哈西中找到其对应的服务器IP
  20. # 找到相对应的redis服务器,如: r = redis.StrictRedis(host='localhost', port=6379, db=0)
  21. # 使用python redis api 链接
  22. # 获取数据,即:
  23. # return self._redis.hget(self._id, name)
  24.  
  25. def __setitem__(self, key, value):
  26. # 根据 self._id ,在一致性哈西中找到其对应的服务器IP
  27. # 使用python redis api 链接
  28. # 设置session
  29. # self._redis.hset(self._id, name, value)
  30.  
  31. def __delitem__(self, key):
  32. # 根据 self._id 找到相对应的redis服务器
  33. # 使用python redis api 链接
  34. # 删除,即:
  35. return self._redis.hdel(self._id, name)

session

十一、表单的验证

在Web程序中往往包含大量的表单验证的工作,如:判断输入是否为空,是否符合规则

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title></title>
  6. <link href="{{static_url("commons.css")}}" rel="stylesheet" />
  7. </head>
  8. <body>
  9. <h1>hello</h1>
  10. <form action="/index" method="post">
  11.  
  12. <p>hostname: <input type="text" name="host" /> </p>
  13. <p>ip: <input type="text" name="ip" /> </p>
  14. <p>port: <input type="text" name="port" /> </p>
  15. <p>phone: <input type="text" name="phone" /> </p>
  16. <input type="submit" />
  17. </form>
  18. </body>
  19. </html>

index.html

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6. from hashlib import sha1
  7. import os, time
  8. import re
  9.  
  10. class MainForm(object):
  11. def __init__(self):
  12. self.host = "(.*)"
  13. self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
  14. self.port = '(\d+)'
  15. self.phone = '^1[3|4|5|8][0-9]\d{8}$'
  16.  
  17. def check_valid(self, request):
  18. form_dict = self.__dict__
  19. for key, regular in form_dict.items():
  20. post_value = request.get_argument(key)
  21. # 让提交的数据 和 定义的正则表达式进行匹配
  22. ret = re.match(regular, post_value)
  23. print key,ret,post_value
  24.  
  25. class MainHandler(tornado.web.RequestHandler):
  26. def get(self):
  27. self.render('index.html')
  28. def post(self, *args, **kwargs):
  29. obj = MainForm()
  30. result = obj.check_valid(self)
  31. self.write('ok')
  32.  
  33. settings = {
  34. 'template_path': 'template',
  35. 'static_path': 'static',
  36. 'static_url_prefix': '/static/',
  37. 'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
  38. 'login_url': '/login'
  39. }
  40.  
  41. application = tornado.web.Application([
  42. (r"/index", MainHandler),
  43. ], **settings)
  44.  
  45. if __name__ == "__main__":
  46. application.listen(8888)
  47. tornado.ioloop.IOLoop.instance().start()

app.py

由于验证规则可以代码重用,所以可以如此定义:

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. import tornado.ioloop
  5. import tornado.web
  6. import re
  7.  
  8. class Field(object):
  9.  
  10. def __init__(self, error_msg_dict, required):
  11. self.id_valid = False
  12. self.value = None
  13. self.error = None
  14. self.name = None
  15. self.error_msg = error_msg_dict
  16. self.required = required
  17.  
  18. def match(self, name, value):
  19. self.name = name
  20.  
  21. if not self.required:
  22. self.id_valid = True
  23. self.value = value
  24. else:
  25. if not value:
  26. if self.error_msg.get('required', None):
  27. self.error = self.error_msg['required']
  28. else:
  29. self.error = "%s is required" % name
  30. else:
  31. ret = re.match(self.REGULAR, value)
  32. if ret:
  33. self.id_valid = True
  34. self.value = ret.group()
  35. else:
  36. if self.error_msg.get('valid', None):
  37. self.error = self.error_msg['valid']
  38. else:
  39. self.error = "%s is invalid" % name
  40.  
  41. class IPField(Field):
  42. REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
  43.  
  44. def __init__(self, error_msg_dict=None, required=True):
  45.  
  46. error_msg = {} # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
  47. if error_msg_dict:
  48. error_msg.update(error_msg_dict)
  49.  
  50. super(IPField, self).__init__(error_msg_dict=error_msg, required=required)
  51.  
  52. class IntegerField(Field):
  53. REGULAR = "^\d+$"
  54.  
  55. def __init__(self, error_msg_dict=None, required=True):
  56. error_msg = {'required': '数字不能为空', 'valid': '数字格式错误'}
  57. if error_msg_dict:
  58. error_msg.update(error_msg_dict)
  59.  
  60. super(IntegerField, self).__init__(error_msg_dict=error_msg, required=required)
  61.  
  62. class CheckBoxField(Field):
  63.  
  64. def __init__(self, error_msg_dict=None, required=True):
  65. error_msg = {} # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
  66. if error_msg_dict:
  67. error_msg.update(error_msg_dict)
  68.  
  69. super(CheckBoxField, self).__init__(error_msg_dict=error_msg, required=required)
  70.  
  71. def match(self, name, value):
  72. self.name = name
  73.  
  74. if not self.required:
  75. self.id_valid = True
  76. self.value = value
  77. else:
  78. if not value:
  79. if self.error_msg.get('required', None):
  80. self.error = self.error_msg['required']
  81. else:
  82. self.error = "%s is required" % name
  83. else:
  84. if isinstance(name, list):
  85. self.id_valid = True
  86. self.value = value
  87. else:
  88. if self.error_msg.get('valid', None):
  89. self.error = self.error_msg['valid']
  90. else:
  91. self.error = "%s is invalid" % name
  92.  
  93. class FileField(Field):
  94. REGULAR = "^(\w+\.pdf)|(\w+\.mp3)|(\w+\.py)$"
  95.  
  96. def __init__(self, error_msg_dict=None, required=True):
  97. error_msg = {} # {'required': '数字不能为空', 'valid': '数字格式错误'}
  98. if error_msg_dict:
  99. error_msg.update(error_msg_dict)
  100.  
  101. super(FileField, self).__init__(error_msg_dict=error_msg, required=required)
  102.  
  103. def match(self, name, value):
  104. self.name = name
  105. self.value = []
  106. if not self.required:
  107. self.id_valid = True
  108. self.value = value
  109. else:
  110. if not value:
  111. if self.error_msg.get('required', None):
  112. self.error = self.error_msg['required']
  113. else:
  114. self.error = "%s is required" % name
  115. else:
  116. m = re.compile(self.REGULAR)
  117. if isinstance(value, list):
  118. for file_name in value:
  119. r = m.match(file_name)
  120. if r:
  121. self.value.append(r.group())
  122. self.id_valid = True
  123. else:
  124. self.id_valid = False
  125. if self.error_msg.get('valid', None):
  126. self.error = self.error_msg['valid']
  127. else:
  128. self.error = "%s is invalid" % name
  129. break
  130. else:
  131. if self.error_msg.get('valid', None):
  132. self.error = self.error_msg['valid']
  133. else:
  134. self.error = "%s is invalid" % name
  135.  
  136. def save(self, request, upload_path=""):
  137.  
  138. file_metas = request.files[self.name]
  139. for meta in file_metas:
  140. file_name = meta['filename']
  141. with open(file_name,'wb') as up:
  142. up.write(meta['body'])
  143.  
  144. class Form(object):
  145.  
  146. def __init__(self):
  147. self.value_dict = {}
  148. self.error_dict = {}
  149. self.valid_status = True
  150.  
  151. def validate(self, request, depth=10, pre_key=""):
  152.  
  153. self.initialize()
  154. self.__valid(self, request, depth, pre_key)
  155.  
  156. def initialize(self):
  157. pass
  158.  
  159. def __valid(self, form_obj, request, depth, pre_key):
  160. """
  161. 验证用户表单请求的数据
  162. :param form_obj: Form对象(Form派生类的对象)
  163. :param request: Http请求上下文(用于从请求中获取用户提交的值)
  164. :param depth: 对Form内容的深度的支持
  165. :param pre_key: Html中name属性值的前缀(多层Form时,内部递归时设置,无需理会)
  166. :return: 是否验证通过,True:验证成功;False:验证失败
  167. """
  168.  
  169. depth -= 1
  170. if depth < 0:
  171. return None
  172. form_field_dict = form_obj.__dict__
  173. for key, field_obj in form_field_dict.items():
  174. print key,field_obj
  175. if isinstance(field_obj, Form) or isinstance(field_obj, Field):
  176. if isinstance(field_obj, Form):
  177. # 获取以key开头的所有的值,以参数的形式传至
  178. self.__valid(field_obj, request, depth, key)
  179. continue
  180. if pre_key:
  181. key = "%s.%s" % (pre_key, key)
  182.  
  183. if isinstance(field_obj, CheckBoxField):
  184. post_value = request.get_arguments(key, None)
  185. elif isinstance(field_obj, FileField):
  186. post_value = []
  187. file_list = request.request.files.get(key, None)
  188. for file_item in file_list:
  189. post_value.append(file_item['filename'])
  190. else:
  191. post_value = request.get_argument(key, None)
  192.  
  193. print post_value
  194. # 让提交的数据 和 定义的正则表达式进行匹配
  195. field_obj.match(key, post_value)
  196. if field_obj.id_valid:
  197. self.value_dict[key] = field_obj.value
  198. else:
  199. self.error_dict[key] = field_obj.error
  200. self.valid_status = False
  201.  
  202. class ListForm(object):
  203. def __init__(self, form_type):
  204. self.form_type = form_type
  205. self.valid_status = True
  206. self.value_dict = {}
  207. self.error_dict = {}
  208.  
  209. def validate(self, request):
  210. name_list = request.request.arguments.keys() + request.request.files.keys()
  211. index = 0
  212. flag = False
  213. while True:
  214. pre_key = "[%d]" % index
  215. for name in name_list:
  216. if name.startswith(pre_key):
  217. flag = True
  218. break
  219. if flag:
  220. form_obj = self.form_type()
  221. form_obj.validate(request, depth=10, pre_key="[%d]" % index)
  222. if form_obj.valid_status:
  223. self.value_dict[index] = form_obj.value_dict
  224. else:
  225. self.error_dict[index] = form_obj.error_dict
  226. self.valid_status = False
  227. else:
  228. break
  229.  
  230. index += 1
  231. flag = False
  232.  
  233. class MainForm(Form):
  234.  
  235. def __init__(self):
  236. # self.ip = IPField(required=True)
  237. # self.port = IntegerField(required=True)
  238. # self.new_ip = IPField(required=True)
  239. # self.second = SecondForm()
  240. self.fff = FileField(required=True)
  241. super(MainForm, self).__init__()
  242.  
  243. #
  244. # class SecondForm(Form):
  245. #
  246. # def __init__(self):
  247. # self.ip = IPField(required=True)
  248. # self.new_ip = IPField(required=True)
  249. #
  250. # super(SecondForm, self).__init__()
  251.  
  252. class MainHandler(tornado.web.RequestHandler):
  253. def get(self):
  254. self.render('index.html')
  255. def post(self, *args, **kwargs):
  256. # for i in dir(self.request):
  257. # print i
  258. # print self.request.arguments
  259. # print self.request.files
  260. # print self.request.query
  261. # name_list = self.request.arguments.keys() + self.request.files.keys()
  262. # print name_list
  263.  
  264. # list_form = ListForm(MainForm)
  265. # list_form.validate(self)
  266. #
  267. # print list_form.valid_status
  268. # print list_form.value_dict
  269. # print list_form.error_dict
  270.  
  271. # obj = MainForm()
  272. # obj.validate(self)
  273. #
  274. # print "验证结果:", obj.valid_status
  275. # print "符合验证结果:", obj.value_dict
  276. # print "错误信息:"
  277. # for key, item in obj.error_dict.items():
  278. # print key,item
  279. # print self.get_arguments('favor'),type(self.get_arguments('favor'))
  280. # print self.get_argument('favor'),type(self.get_argument('favor'))
  281. # print type(self.get_argument('fff')),self.get_argument('fff')
  282. # print self.request.files
  283. # obj = MainForm()
  284. # obj.validate(self)
  285. # print obj.valid_status
  286. # print obj.value_dict
  287. # print obj.error_dict
  288. # print self.request,type(self.request)
  289. # obj.fff.save(self.request)
  290. # from tornado.httputil import HTTPServerRequest
  291. # name_list = self.request.arguments.keys() + self.request.files.keys()
  292. # print name_list
  293. # print self.request.files,type(self.request.files)
  294. # print len(self.request.files.get('fff'))
  295.  
  296. # obj = MainForm()
  297. # obj.validate(self)
  298. # print obj.valid_status
  299. # print obj.value_dict
  300. # print obj.error_dict
  301. # obj.fff.save(self.request)
  302. self.write('ok')
  303.  
  304. settings = {
  305. 'template_path': 'template',
  306. 'static_path': 'static',
  307. 'static_url_prefix': '/static/',
  308. 'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
  309. 'login_url': '/login'
  310. }
  311.  
  312. application = tornado.web.Application([
  313. (r"/index", MainHandler),
  314. ], **settings)
  315.  
  316. if __name__ == "__main__":
  317. application.listen(8888)
  318. tornado.ioloop.IOLoop.instance().start()

第一个web框架tornado的更多相关文章

  1. Python web框架 Tornado异步非阻塞

    Python web框架 Tornado异步非阻塞   异步非阻塞 阻塞式:(适用于所有框架,Django,Flask,Tornado,Bottle) 一个请求到来未处理完成,后续一直等待 解决方案: ...

  2. Python Web框架 tornado 异步原理

    Python Web框架 tornado 异步原理 参考:http://www.jb51.net/article/64747.htm 待整理

  3. 2、基于wsgiref模块DIY一个web框架

    一 web框架 Web框架(Web framework)是一种开发框架,用来支持动态网站.网络应用和网络服务的开发.这大多数的web框架提供了一套开发和部署网站的方式,也为web行为提供了一套通用的方 ...

  4. Go语言笔记[实现一个Web框架实战]——EzWeb框架(一)

    Go语言笔记[实现一个Web框架实战]--EzWeb框架(一) 一.Golang中的net/http标准库如何处理一个请求 func main() { http.HandleFunc("/& ...

  5. 手把手和你一起实现一个Web框架实战——EzWeb框架(二)[Go语言笔记]Go项目实战

    手把手和你一起实现一个Web框架实战--EzWeb框架(二)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 上一篇文章我们实现了框架的雏形,基本地 ...

  6. 手把手和你一起实现一个Web框架实战——EzWeb框架(三)[Go语言笔记]Go项目实战

    手把手和你一起实现一个Web框架实战--EzWeb框架(三)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 本篇代码,请选择demo3 这一篇文章 ...

  7. 手把手和你一起实现一个Web框架实战——EzWeb框架(四)[Go语言笔记]Go项目实战

    手把手和你一起实现一个Web框架实战--EzWeb框架(四)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 这一篇文章主要实现路由组功能.实现路由 ...

  8. 手把手和你一起实现一个Web框架实战——EzWeb框架(五)[Go语言笔记]Go项目实战

    手把手和你一起实现一个Web框架实战--EzWeb框架(五)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 本篇代码,请选择demo5 中间件实现 ...

  9. 异步非阻塞IO的Python Web框架--Tornado

    Tornado的全称是Torado Web Server,从名字上就可知它可用作Web服务器,但同时它也是一个Python Web的开发框架.最初是在FriendFeed公司的网站上使用,FaceBo ...

随机推荐

  1. java 成长之路

    分享总结 title: java 成长之路 tags: grammar_cjkRuby: true 经验差异 1-3年 要求 建议 3-5年 建议 5年+ 经验差异 最近一年比较忙,经历了创业公司的倒 ...

  2. MongoDB 内嵌文档

    MongoDB是文档型的数据库系统,doc是MongoDB的数据单位,每个doc相当于关系型数据库的数据行(row),doc和row的区别在于field的原子性:row中的column是不和分割的原子 ...

  3. matlab 循环读入多个mat文件组合成一个mat文件

    今天做实验,需要到这个功能,在朋友的告知下,写了代码,在此留个标记 clc clear load('F:\效果对比\colorhist\1.mat'); a=ans; a=a'; : filename ...

  4. PHP程序员如何理解IoC/DI

    思想是解决问题的根本 思想必须转换成习惯构建一套完整的思想体系是开发能力成熟的标志 详情请点击

  5. GROUP函数-GROUP_ID,GROUPING,GROUPING_ID

    GROUP_ID 首先我们看看官方的解释: 大意是GROUP_ID用于区分相同分组标准的分组统计结果. 解释起来比较抽象,下面我们来看看具体的案例. 例1:单一分组 SQL> select gr ...

  6. php通过判断来源主机头进行防盗链

    check.php <html> <body> <form action="test.php" method="post"> ...

  7. 小身材大用途,用PrimusUI驾驭你的页面

    “PrimusUI”是自己在借鉴了如今网上很多开源的UI库,再经过自己整理加工的一个简单代码集合. 每个功能块的CSS代码都很少,力求简单易懂,低门槛,代码可根据自己实际情况轻易修改,改到符合自己场景 ...

  8. 相克军_Oracle体系_随堂笔记016-参数文件及数据库的启动和关闭

    参数文件: spfile<SID>.ora    动态参数文件,是二进制文件,9i以后引入并建议使用 init<SID>.ora    静态参数文件,是文本文件 动态参数,部分 ...

  9. 关于jqGrig如何写自定义格式化函数将JSON数据的字符串转换为表格各个列的值

    首先介绍一下jqGrid是一个jQuery的一个表格框架,现在有一个需求就是将数据库表的数据拿出来显示出来,分别有id,name,details三个字段,其中难点就是details字段,它的数据是这样 ...

  10. 从零开始学 Java - Spring 集成 ActiveMQ 配置(二)

    从上一篇开始说起 上一篇从零开始学 Java - Spring 集成 ActiveMQ 配置(一)文章中讲了我关于消息队列的思考过程,现在这一篇会讲到 ActivMQ 与 Spring 框架的整合配置 ...