概述

Tornado 是 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

框架使用

一、快速上手

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

执行过程:

  • 第一步:执行脚本,监听 8888 端口

  • 第二步:浏览器客户端访问 /index  -->  http://127.0.0.1:8888/index

  • 第三步:服务器接受请求,并交由对应的类处理该请求

  • 第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法

  • 第五步:方法返回值的字符串内容发送浏览器

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

异步非阻塞事例

注意: self.render('xx.html')  等, 仍然会执行完后面的语句再加载页面

二、路由系统

路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架。

其他很多框架均是 url 对应函数,Tornado中每个url对应的是一个类。

  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. class StoryHandler(tornado.web.RequestHandler):
  12. def get(self, story_id):
  13. self.write("You requested the story " + story_id)
  14.  
  15. class BuyHandler(tornado.web.RequestHandler):
  16. def get(self):
  17. self.write("buy.wupeiqi.com/index")
  18.  
  19. application = tornado.web.Application([
  20. (r"/index", MainHandler),
  21. (r"/story/([0-9]+)", StoryHandler),
  22. ])
  23.  
  24. application.add_handlers('buy.5poi.com$', [
  25. (r'/index',BuyHandler),
  26. ])
  27.  
  28. if __name__ == "__main__":
  29. application.listen(80)
  30. tornado.ioloop.IOLoop.instance().start()

Tornado中原生支持二级域名的路由,如:

  1. application = tornado.web.Application([
  2. (r"/index", MainHandler),
  3. ])
  4.  
  5. application.add_handlers('buy.5poi.com$', [
  6. (r'/index',BuyHandler),
  7. ])

三、模板引擎

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

Tornado 的模板支持“控制语句”和“表达语句”。

控制语句是使用 {% 和 %} 包起来的 例如 {% if len(items) > 2 %}

表达语句是使用 {{ 和 }} 包起来的,例如 {{ items[0] }}

控制语句和对应的 Python 语句的格式基本完全相同。

我们支持 ifforwhile 和 try,这些语句逻辑结束的位置需要用 {% end %} 做标记。

还通过 extends 和 block 语句实现了模板继承。这些在 template 模块 的代码文档中有着详细的描述。

注:在使用模板前需要在setting中设置模板路径:"template_path" : "views相应的文件夹"

1、基本使用

  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("index.html", list_info = [11,22,33])
  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()

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、母版  extends继承页面  常用于页面整体布局

  1. {% extends 'layout.html'%} 继承layout.html
  2.  
  3. {% block body1 %} body1 替换到继承的 layout.html 中的body1
  4. <h1>Index</h1>
  5. {% end %}
  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.html

  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、导入 include  常用于导入小组件

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

导入

  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

a. 定义

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

uimethods.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 '<h1>alex</h1>'
  10. #return escape.xhtml_escape('<h1>alex</h1>')

uimodules.py

b. 注册

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

c. 使用

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

注意:  想要静态文件自定义处理,不通过 static_url('xxx') 访问:

  1. application = tornado.web.Application([
  2. (r"/(upload/.*.jpg)", tornado.web.StaticFileHandler, dict(path=settings['static_path'])),
  3. ],**settings)

五、cookie

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

  1. self.set_cookie(self, name, value, domain=None, expires=None, path="/", expires_days=None, **kwargs)
name Cookie的Key
value Cookie的value
domain 生效的域名
expires 以秒为过期时间,默认从 1970-01-01T00:00:10.000Z
path 生效路径
expires_days 以天数过期时间,如果设置为 None 则关闭浏览器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!")

补充:

  1. 关闭浏览器,Cookie就失效, 设置
  2. expires_days=None
  3.  
  4. 注意的是不要同时传递expires expires_days
  5. self.set_cookie('user_id', '', expires_days=None, expires=某个时间)
  6.  
  7. expires_day=None, 或者expires_day=3, 3天,
  8. 都不会影响expires的, 因为expiresexpires_days 的优先级高
  9.  
  10. self.set_cookie('user_id', '', expires=time.time()+15*60) 15分钟过期
  11.  
  12. path='/' 表示全局
  13. self.set_cookie('user_id', '', path='/',expires=time.time()+15*60)

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. setting = {
  10.   'cookie_secret':'58ksjfSmxoi'
  11. }
  12.  
  13. application = tornado.web.Application([
  14. (r"/", MainHandler),
  15. ],**setting)
  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的本质是:

写cookie过程:

  • 将值进行base64加密

  • 对除值以外的内容进行签名,哈希算法(无法逆向解析)

  • 拼接 签名 + 加密值

读cookie过程:

  • 读取 签名 + 加密值

  • 对签名进行验证

  • base64解密,获取值内容

注:许多API验证机制和安全cookie的实现机制相同。

  1. import tornado.web
  2. import tornado.ioloop
  3.  
  4. class IndexHandler(tornado.web.RequestHandler):
  5. def get(self):
  6. self.set_secure_cookie('username', 'ansheng')
  7. self.set_secure_cookie('password', 'hello')
  8. self.render('index.html')
  9.  
  10. def post(self, *args, **kwargs):
  11. username = self.get_argument('username', None)
  12. password = self.get_argument('password', None)
  13. cooike_user = str(self.get_secure_cookie('username'), encoding='utf-8')
  14. cooike_pass = str(self.get_secure_cookie('password'), encoding='utf-8')
  15. if username == cooike_user and password == cooike_pass:
  16. self.write('Hello ' + cooike_user)
  17. else:
  18. self.write('用户名或密码错误')
  19.  
  20. settings = {
  21. 'template_path': 'template',
  22. }
  23.  
  24. application = tornado.web.Application([
  25. (r'/', IndexHandler),
  26. ], **settings,
  27. cookie_secret="508C934B83CC")
  28.  
  29. if __name__ == '__main__':
  30. application.listen(8000)
  31. 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. }

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

六、CSRF

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

1.设置:

  1. settings = { "xsrf_cookies": True, }

页面加载后,cookie里有一个为 _xsrf = 'xxxxxxxxxxxx' 的值.

需要传递_xsrf的值请求才能通过.

2.form 表单:

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

3.ajax

  1. <!-- 一个获取cookie的方法 !-->
  2. function getCookie(name) {
  3.   var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
  4.   return r ? r[1] : undefined;
  5. }
  6.  
  7. <!-- ajax提交数据中,写上获取的cookie值 !-->
  8. var cookie= getCookie("_xsrf");
  9. $.post({
  10.   url:'/index',
  11.   data:{'content':'v1', '_xsrf' :cookie},
  12.   success:function (callbakc) {
  13.   console.log(callbakc)
  14.   }
  15. });

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

七、上传文件

上传文件标签:   <input type="file" name="file" id="my_file" />

后台获取:      file_metas = self.request.files['file']

  1. 获取格式:
  2. [{'body': b'xxxxxxxxx', 'content_type': 'text/plain', 'filename': '文件名.txt/...'},...多个文件]

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>

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

Python

2、AJAX上传

1.基于XMLHttpRequest

  1. <input type="file" id="img" />
  2. <input type="button" onclick="UploadFile();" />
  3. <script>
  4. function UploadFile(){
  5. var fileObj = document.getElementById("img")[0].files[0];
  6.  
  7. var form = new FormData();
  8. form.append("k1", "v1");
  9. form.append("fff", fileObj);
  10.  
  11. var xhr = new XMLHttpRequest();
  12. xhr.open("post", '/index', true);
  13. xhr.send(form);
  14. }
  15. </script>

2.基于Jquery

  1. function UploadFile() {
  2. var fileObj = $("#file1")[0].files[0];
  3.  
  4. var form = new FormData();
  5. form.append("k1", "v1");
  6. form.append("fff", fileObj);
  7.  
  8. $.ajax({
  9. type:'post',
  10. url:'/upload',
  11. data:form,
  12. processData: false, // tell jQuery not to process the data
  13. contentType: false, // tell jQuery not to set contentType
  14. });
  15. }

3.基于iframe

  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>
  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="50" type="file" />
  37. <input id="formsubmit" type="submit" value="Send File" />
  38. </form>
  39.  
  40. <div id="textarea">
  41. </div>

扩展基于iframe实现ajax上传

  1. function bindChangeAvatar1() {
  2. $('#avatarImg').change(function () {
  3. var file_obj = $(this)[0].files[0];
  4. $('#prevViewImg')[0].src = window.URL.createObjectURL(file_obj)
  5. })
  6. }
  7.  
  8. function bindChangeAvatar2() {
  9. $('#avatarImg').change(function () {
  10. var file_obj = $(this)[0].files[0];
  11. var reader = new FileReader();
  12. reader.readAsDataURL(file_obj);
  13. reader.onload = function (e) {
  14. $('#previewImg')[0].src = this.result;
  15. };
  16. })
  17. }
  18.  
  19. function bindChangeAvatar3() {
  20. $('#avatarImg').change(function () {
  21. var file_obj = $(this)[0].files[0];
  22. var form = new FormData();
  23. form.add('img_upload', file_obj);
  24.  
  25. $.ajax({
  26. url: '',
  27. data: form,
  28. processData: false, // tell jQuery not to process the data
  29. contentType: false, // tell jQuery not to set contentType
  30. success: function (arg) {
  31.  
  32. }
  33. })
  34. })
  35. }
  36.  
  37. function bindChangeAvatar4() {
  38. $('#avatarImg').change(function () {
  39. $(this).parent().submit();
  40.  
  41. $('#upload_iframe').load(function () {
  42. var iframeContents = this.contentWindow.document.body.innerText;
  43. iframeContents = JSON.parse(iframeContents);
  44. if (iframeContents.status) {
  45. $('#previewImg').attr('src', '/' + iframeContents.data);
  46. }
  47. })
  48.  
  49. })
  50. }

其他

八、验证码

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

安装图像处理模块:

  1. pip3 install pillow

示例截图:

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

  1. class CheckcodeHandler(BaseHandler):
  2. def get(self, *args, **kwargs):
  3. import io
  4. import check_code
  5. mstream = io.BytesIO()
  6. img, code = check_code.create_validate_code()
  7. self.session['code'] = code //生成验证码存放在session中
  8. img.save(mstream, "GIF")
  9. self.write(mstream.getvalue())
  10.  
  11. ************************************************
  12. <p>
  13. <input name='code' type="text" placeholder="验证码" />
  14. <img src="/check_code" onclick='ChangeCode();' id='imgCode'>
  15. </p>
  16.  
  17. <script type="text/javascript">
  18. function ChangeCode() {
  19. var code = document.getElementById('imgCode');
  20. code.src += '?'; //点击一次图片,换一次激活码
  21. }
  22. </script>

九、异步非阻塞

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

自定义Web组件

一、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'] = 'alex'
  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 = "alex"
  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.  
  4. import tornado.ioloop
  5. import tornado.web
  6.  
  7. container = {}
  8.  
  9. class Session():
  10.  
  11. def __init__(self,handler):
  12. self.handler = handler
  13. self.random_str = None
  14.  
  15. def get_random_str(self):
  16. import hashlib
  17. import time
  18. hash = hashlib.md5()
  19. hash.update(bytes(str(time.time()), encoding='utf-8'))
  20. random_str = hash.hexdigest()
  21. return random_str
  22.  
  23. def __setitem__(self, key, value):
  24. if not self.random_str:
  25. random_str = self.handler.get_cookie('_session', None)
  26. if not random_str:
  27. random_str = self.get_random_str()
  28. container[random_str] = {}
  29. else:
  30. if random_str not in container.keys():
  31. random_str = self.get_random_str()
  32. container[random_str] = {}
  33. self.random_str = random_str
  34.  
  35. container[self.random_str][key] = value
  36. self.handler.set_cookie('_session', self.random_str)
  37.  
  38. def __getitem__(self, key):
  39.  
  40. random_str = self.handler.get_cookie('_session', None)
  41. if not random_str:
  42. return None
  43. user_info_dict = container.get(random_str, None)
  44. if not user_info_dict:
  45. return None
  46. value = user_info_dict.get(key,None)
  47. return value
  48.  
  49. class BaseHandler(tornado.web.RequestHandler):
  50. def initialize(self):
  51. self.session = Session(self)
  52.  
  53. class MainHandler(BaseHandler):
  54. def get(self):
  55.  
  56. print(self.cookies)
  57. self.session['kkk'] = ''
  58.  
  59. self.write("Hello, world")
  60.  
  61. application = tornado.web.Application([
  62. (r"/index", MainHandler),
  63. ])
  64.  
  65. if __name__ == "__main__":
  66. application.listen(8888)
  67. tornado.ioloop.IOLoop.instance().start()

demo

二、表单验证

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

例子1:

  1. import tornado.ioloop
  2. import tornado.web
  3. from hashlib import sha1
  4. import os, time
  5. import re
  6.  
  7. class MainForm(object):
  8. def __init__(self):
  9. self.host = "(.*)"
  10. 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}$"
  11. self.port = '(\d+)'
  12. self.phone = '^1[3|4|5|8][0-9]\d{8}$'
  13.  
  14. def check_value(self,handler):
  15. flag = True
  16. input_dict = {}
  17. for key,regular in self.__dict__.items():
  18. input_value = handler.get_argument(key)
  19. val = re.match(regular,input_value)
  20. if not val:
  21. flag = False
  22. input_dict[key] = input_value
  23. print(val,input_dict[key],regular)
  24. return flag,input_dict
  25.  
  26. class MainHandler(tornado.web.RequestHandler):
  27. def get(self):
  28. self.render("index.html")
  29. def post(self, *args, **kwargs):
  30. obj = MainForm()
  31. val,input_dict = obj.check_value(self)
  32. print(val,input_dict)
  33.  
  34. settings = {
  35. 'template_path': 'views', # 模版路径的配置
  36. 'static_path' : 'static', # 静态文件路径
  37. # 'static_url_prefix' : '/sss/',
  38. }
  39.  
  40. application = tornado.web.Application([
  41. (r"/index", MainHandler),
  42. ],**settings)
  43.  
  44. if __name__ == "__main__":
  45. application.listen(8889)
  46. 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.  
  7. </head>
  8. <body>
  9. <h1>hello</h1>
  10. <form action="/index" method="post">
  11.  
  12. <p>hostname: <input type="text" name="host" placeholder="host"/> </p>
  13. <p>ip: <input type="text" name="ip" placeholder="ip"/> </p>
  14. <p>port: <input type="text" name="port" placeholder="port" /> </p>
  15. <p>phone: <input type="text" name="phone" placeholder="phone"/> </p>
  16. <input type="submit" />
  17. </form>
  18. </body>
  19. </html>

index.html

例子2:

  1. import tornado.ioloop
  2. import tornado.web
  3. from hashlib import sha1
  4. import os, time
  5. import re
  6.  
  7. class IPField():
  8. REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
  9. def __init__(self,required=True,error_dict=None):
  10.  
  11. self.error_dict = {} #错误信息
  12. if error_dict:
  13. self.error_dict.update(error_dict) #用户自定错误信息
  14.  
  15. self.required = required
  16. self.value = None
  17. self.error = None
  18. self.is_valid = False
  19.  
  20. def validate(self,name,input_value):
  21.  
  22. if not self.required: # 可以为空
  23. self.value = input_value
  24. self.is_valid = True
  25. else:
  26. #1.不能为空,用户输入为空
  27. #2.用户输入错误,
  28. #3.用户输入正确
  29. if not input_value:
  30. if self.error_dict.get('required',None):
  31. self.error = self.error_dict['required']
  32. else:
  33. self.error = '%s is requires '%(name)
  34. else:
  35. val = re.match(IPField.REGULAR,input_value)
  36. if not val: #用户输入错误 , re返回None
  37. if self.error_dict.get('valid', None):
  38. self.error = self.error_dict['valid']
  39. else:
  40. self.error = '%s is valid ' % (name)
  41. else:
  42. self.value = input_value
  43. self.is_valid = True
  44.  
  45. class CheckBoxField():
  46.  
  47. def __init__(self,required=True,error_dict=None):
  48.  
  49. self.error_dict = {}
  50. if error_dict:
  51. self.error_dict.update(error_dict)
  52. self.required = required
  53. self.value = None
  54. self.error = None
  55. self.is_valid = False
  56.  
  57. def validate(self,name,input_value):
  58. if not self.required:
  59. self.value = input_value
  60. self.is_valid = True
  61. else:
  62. if not input_value:
  63. if self.error_dict.get('required',None):
  64. self.error = self.error_dict['required']
  65. else:
  66. self.error = '%s is requires '%(name)
  67. else:
  68. self.value = input_value
  69. self.is_valid = True
  70.  
  71. class FileField():
  72. REGULAR = "^(\w+\.pdf)|(\w+\.mp3)|(\w+\.py)$"
  73.  
  74. def __init__(self, required=True, error_dict=None):
  75.  
  76. self.error_dict = {}
  77. if error_dict:
  78. self.error_dict.update(error_dict)
  79. self.required = required
  80. self.value = []
  81. self.error = None
  82. self.is_valid = True
  83. self.success_file_name_list = []
  84. self.name = None
  85.  
  86. def validate(self, name, all_file_name_list):
  87. self.name = name
  88. if not self.required:
  89. self.value = all_file_name_list
  90. else:
  91. if not all_file_name_list:
  92. self.is_valid = False
  93. if self.error_dict.get('required', None):
  94. self.error = self.error_dict['required']
  95. else:
  96. self.error = '%s is requires ' % (name)
  97. else:
  98. for file_name in all_file_name_list:
  99. val = re.match(FileField.REGULAR, file_name)
  100. if not val:
  101. self.is_valid = False
  102. if self.error_dict.get('valid', None):
  103. self.error = self.error_dict['valid']
  104. else:
  105. self.error = '%s is valid ' % (name)
  106. break
  107. else:
  108. self.value.append(file_name)
  109.  
  110. def save(self,handler,path='upload'):
  111. file_list = handler.request.files.get(self.name)
  112. for file in file_list:
  113. file_name = file['filename']
  114. file_path = os.path.join(path,file_name)
  115. if file_name and file_name in self.value:
  116. with open(file_path,'wb') as up:
  117. up.write(file['body'])
  118.  
  119. class BaseForm():
  120. def check_value(self,handler):
  121. flag = True
  122. success_value_dict = {}
  123. error_message_dict = {}
  124. for key,regular in self.__dict__.items():
  125. if type(regular) == CheckBoxField:
  126. input_value = handler.get_arguments(key) # checkbox取值
  127. elif type(regular) == FileField:
  128. file_list = handler.request.files.get(key,[]) # 文件对象
  129. input_value = []
  130. for item in file_list:
  131. input_value.append(item['filename'])
  132. else:
  133. input_value = handler.get_argument(key)
  134.  
  135. regular.validate(key,input_value)
  136. if regular.is_valid:
  137. success_value_dict[key] = regular.value
  138. else:
  139. flag=False
  140. error_message_dict[key] = regular.error
  141.  
  142. return flag,success_value_dict,error_message_dict
  143.  
  144. class HomeForm(BaseForm):
  145. def __init__(self):
  146. self.ip = IPField(required=True,error_dict={'required':'ip 不能为空!' , 'valid':'格式错误!!'})
  147. self.aihao = CheckBoxField(required=False,error_dict={'required':'checkbox 不能为空!' , 'valid':'格式错误!!'})
  148. self.fafafa = FileField(required=True)
  149.  
  150. class HomeHandler(tornado.web.RequestHandler):
  151. def get(self, *args, **kwargs):
  152. self.render('home.html',error_message = None)
  153. def post(self, *args, **kwargs):
  154. obj = HomeForm()
  155. val, success_value_dict , error_message_dict = obj.check_value(self)
  156. print(val, success_value_dict , error_message_dict)
  157. if val : #所有验证通过 文件上传
  158. obj.fafafa.save(self)
  159. self.render('home.html',error_message = None)
  160.  
  161. settings = {
  162. 'template_path': 'views', # 模版路径的配置
  163. 'static_path' : 'static', # 静态文件路径
  164. # 'static_url_prefix' : '/sss/',
  165. }
  166.  
  167. application = tornado.web.Application([
  168. (r"/home", HomeHandler),
  169. ],**settings)
  170.  
  171. if __name__ == "__main__":
  172. application.listen(8889)
  173. 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.  
  7. </head>
  8. <body>
  9. <h1>hello</h1>
  10. <form action="/home" method="post" enctype="multipart/form-data">
  11.  
  12. <p>ip: <input type="text" name="ip" placeholder="ip"/> </p>
  13. <input type="checkbox" name='aihao' value="1">篮球
  14. <input type="checkbox" name='aihao' value="2">足球
  15. <input type="checkbox" name='aihao' value="3">乒乓球
  16. <input type="file" name="fafafa">
  17. <input type="file" name="fafafa">
  18. {% if error_message %}
  19. <span style="color: red">{{ error_message['ip'] }}</span>
  20. {% end %}
  21. <input type="submit" />
  22. </form>
  23. </body>
  24. </html>

home.html

三、分页

核心: 后端生成标签,返回给前端显示。

效果:

首页 上一页  1 2 3 4 5 7 8 9 10 11 下一页 尾页

首页 上一页  2 3 4 5 6 8 9 10 11 12 下一页 尾页

首页 上一页  2 3 4 5 6 7 8 9 10 11 下一页 尾页

设计思路:

1.当总页数 all_page 小于11时,

  => 是,( 直接生成全部页码 ) 设置 起始页s = 1 , 结束页t = all_page

  => 否,当当前页current_page 小于6时,(页码不发生改变)

      => 是,  s = 1 , t = 11

      => 否, 当current_page + 5 < all_page 时,

          => 是 , s = current_page - 5 , t = current_page + 5

=> 否 , ( 此时当前页为最后5页 ,不再往后生成页码 ,保持原来不变)  s = all_page - 10 , t = all_page

2.当前页为1时,点击上一页,首页不跳转,当前页为最后一页时,点击下一页,尾页不跳转。href='javascript:void(0)'

3.字典存储生成的a标签, join()连接列表每项,生成字符串标签返回前端

4.前端显示,输出原始标签字符串  {%raw str_page %}

  1. def page_str(self,base_url):
  2. if self.all_page < 11:
  3. s = 1
  4. t = self.all_page
  5. else:
  6. if self.current_page < 6:
  7. s = 1
  8. t = 11
  9. else:
  10. if (self.current_page + 5) < self.all_page:
  11. s = self.current_page - 5
  12. t = self.current_page + 5
  13. else:
  14. s = self.all_page - 10
  15. t = self.all_page
  16. list_page = []
  17.  
  18. # 首页
  19. first_page = "<a href='%s1'>首页</a>" % (base_url)
  20. list_page.append(first_page)
  21.  
  22. # 上一页
  23. if self.current_page == 1:
  24. pre_page = "<a href='javascript:void(0)'>上一页</a>"
  25. else:
  26. pre_page = "<a href='%s%s'>上一页</a>" % (base_url, self.current_page -1 ,)
  27. list_page.append(pre_page)
  28.  
  29. # 页码生成
  30. for i in range(s, t + 1):
  31. if i == self.current_page:
  32. temp = "<a class='active-paper' href='%s%s'>%s</a>" % (base_url,i, i)
  33. else:
  34. temp = "<a href='%s%s'>%s</a>" % (base_url , i, i)
  35. list_page.append(temp)
  36.  
  37. # 下一页
  38. if self.current_page >= self.all_page:
  39. next_page = "<a href='javascript:void(0)'>下一页</a>"
  40. else:
  41. next_page = "<a href='%s%s'>下一页</a>" % (base_url, self.current_page + 1,)
  42. list_page.append(next_page)
  43.  
  44. # 尾页
  45. end_page = "<a href='%s%s'>尾页</a>" % (base_url,self.all_page)
  46. list_page.append(end_page)
  47.  
  48. # 页面跳转
  49. pagejump_input = """<input type="text"><a onclick="pageNumjump(this,'%s')">Go</a>"""%(base_url)
  50. pagejump_script = """
  51. <script>
  52. function pageNumjump(ths,base_url){
  53. var pagenum = ths.previousElementSibling.value;
  54. if (pagenum.trim().length > 0){
  55. location.href = base_url+pagenum
  56. }
  57. }
  58. </script>
  59. """
  60. list_page.append(pagejump_input)
  61. list_page.append(pagejump_script)
  62.  
  63. # 数据拼接 返回
  64. str_page = ''.join(list_page) # 列表连接成为字符串
  65. return str_page

仿:chouti.com分页显示:

上一页  1 ... 4 5 6 7 8 9 10  下一页

  1. def page_str(self,base_url):
  2. if self.all_page < 10:
  3. s = 1
  4. t = self.all_page
  5. else:
  6. if self.current_page < 7:
  7. s = 1
  8. t = 10
  9. else:
  10. if self.all_page - self.current_page >= 4 :
  11. s = self.current_page - 3
  12. t = self.current_page + 4
  13. else:
  14. s = self.all_page - 6
  15. t = self.all_page
  16. list_page = []
  17.  
  18. # 上一页
  19. if self.current_page != 1:
  20. pre_page = "<a href='%s%s' class='pageedg'>上一页</a>" % (base_url, self.current_page - 1,)
  21. list_page.append(pre_page)
  22.  
  23. # 页码生成
  24. # 生成 1 , ... ,
  25. if( self.current_page >= 7 ):
  26. temp = "<a class='pageNum' href='%s%s'>%s</a>" % (base_url, 1, 1)
  27. list_page.append(temp)
  28. temp = "<span class='ignore' >...</span>"
  29. list_page.append(temp)
  30.  
  31. for i in range(s, t + 1):
  32. if i == self.current_page:
  33. temp = "<span class='active-page' href='%s%s'>%s</span>" % (base_url,i, i)
  34. else:
  35. temp = "<a href='%s%s' class='pageNum'>%s</a>" % (base_url , i, i)
  36. list_page.append(temp)
  37.  
  38. # 下一页
  39. if self.current_page < self.all_page:
  40. next_page = "<a href='%s%s' class='pageedg'>下一页</a>" % (base_url, self.current_page + 1,)
  41. list_page.append(next_page)
  42.  
  43. str_page = ''.join(list_page) # 列表连接成为字符串
  44. return str_page

【Python之路】第十六篇--Web框架之Tornado的更多相关文章

  1. [Python笔记]第十六篇:web框架之Tornado

    Tornado是一个基于python的web框架,xxxxx 安装 python -m pip install tornado 第一个Tornado程序 安装完毕我们就可以新建一个app.py文件,放 ...

  2. Python之路(第二十六篇) 面向对象进阶:内置方法

    一.__getattribute__ object.__getattribute__(self, name) 无条件被调用,通过实例访问属性.如果class中定义了__getattr__(),则__g ...

  3. Python之路(第十六篇)xml模块、datetime模块

    一.xml模块 xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单, xml比较早,早期许多软件都是用xml,至今很多传统公司如金融行业的很多系统的接口还主要 ...

  4. Python之路【第六篇】:socket

    Python之路[第六篇]:socket   Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字&quo ...

  5. [Python之路] 使用装饰器给Web框架添加路由功能(静态、动态、伪静态URL)

    一.观察以下代码 以下来自 Python实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦) 中的mini_frame最后版本的代码: import time def in ...

  6. 【Python之路】第十五篇--Web框架

    Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. #!/usr/bin/env python #coding:utf- ...

  7. Python之路【第六篇】python基础 之面向对象进阶

    一 isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象  和  issubclass(su ...

  8. Python自动化 【第十六篇】:JavaScript作用域和Dom收尾

    本节内容: javascript作用域 DOM收尾 JavaScript作用域 JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走 ...

  9. Python之路【第六篇】python基础 之面向对象(一)

    一.三大编程范式 1.面向过程编程 2.函数式编程 3.面向对象编程 二.编程进化论 1.编程最开始就是无组织无结构,从简单控制流中按步写指令 2.从上述的指令中提取重复的代码块或逻辑,组织到一起(比 ...

随机推荐

  1. Jersey 1.18 API文档

    Jersey 1.18 API文档 我自己制作了Jersey 1.18 API CHM文档, 下载地址见: http://download.csdn.net/detail/chszs/7334869 ...

  2. Activity具体解释(生命周期、启动方式、状态保存,全然退出等)

    一.什么是Activity? 简单的说:Activity就是布满整个窗体或者悬浮于其它窗体上的交互界面. 在一个应用程序中通常由多个Activity构成,都会在Manifest.xml中指定一个主的A ...

  3. VirtualBox实现宿主机和虚拟机之间网络的通讯

    摘要:实现宿主机和虚拟机之间网络的通讯 环境: 宿主机操作系统            WindowsXP 虚拟机软件                    VirtualBox 虚拟机操作系统     ...

  4. 窥探try ... catch与__try ... __except的区别

    VC中的这两个东西肯定谁都用过, 不过它们之间有什么区别, 正好有时间研究了一下, 如果有错误欢迎拍砖.基于VC2005, 32位XP 平台测试通过. 估计对于其他版本的VC和操作系统是不通用的. 1 ...

  5. ios 中尝试多次请求

    -(void)tryRun { tryTimes++; id obj = [ASODataManager getAppleAccount]; if (obj) { __block FirstViewC ...

  6. Python 标准库 BaseHTTPServer 中文翻译

    Python 标准库 BaseHTTPServer 中文翻译. 注意: BaseHTTPServer模块在Python3中已被合并到http.server,当转换你的资源为 Python3 时 2to ...

  7. TensorFlow学习笔记 速记2 报错:failed call to cuDevicePrimaryCtxRetain: CUDA_ERROR_INVALID_DEVICE

    版本: tensorflow-gpu 原因: 在创建session时没有使用我想让它用的gpu 解决方案: 1. 在python程序中: import os os.environ["CUDA ...

  8. Silverlight实例教程 - Validation数据验证开篇

    Silverlight 4 Validation验证实例系列 Silverlight实例教程 - Validation数据验证开篇 Silverlight实例教程 - Validation数据验证基础 ...

  9. C6455 CSL_EMIF详解

    C6455 CSL_EMIF详解 原网址http://www.61ic.com/Article/C6000/C64X/201303/47507.html C6455CSL详解 和DSP6455的EMI ...

  10. 初识Quartz(一)

    首先需要一个任务: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package quartz_proj ...