web.py 是一个小巧灵活的 Python 框架,它简单而且功能强大。0.4 是支持
Python 2 的最后一个版本,接下来的版本将不再支持 Python 2。更新内容如下:

简介

一、web开发介绍

1、web开发概述

  • 修复 Python 3 兼容性问题
  • 取消对 Python<2.7 的支持
  • 允许从 http 修补程序请求获取表单数据
  • 仅在非默认数据的情况下存储新的会话数据
  • 支持 SameSite cookie 属性
  • cookie 的过期时间现在设置为与 session timeout 相同
  • 支持 SQLite 数据库的
    url,如 sqlite:///mydb.sqlite、sqlite:////absolute/path/mydb.sqlite
  • 允许 web.form.input() 中的 HTML 5 表单输入元素
  • 为不同类型添加更多的表单类:Email、Url、Number、Range、Color、Search、Telephone 和
    Datalist
  • 目录 experimental/ 被删除了
  • 模块 web/webopenid.py 已经被删除,它使用了旧 python-OpenID 模块
  • 修复了请求中的 Unicode
  • 修复了 Templator 中导致错误的内联注释
  • 修复 IP 地址,其中包含的空间不应通过验证
  • 修复了使用 boto 发送电子邮件的不正确的参数顺序
  • 修复了 DiskStore 并发问题
  • 修复第一次代码更改时,Reloader 不捕获 APP 模块的问题

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

c/s结构和b/s结构,客户机/服务器结构和浏览器/服务器结构即web开发。

详情见发布日志:

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

b/s结构的优势:

不用通知客户端安装某个软件

不需要通知客户端升级

可以轻松的实现跨平台。

下载与安装

访问网站的过程:用户向web服务器发送请求,然后返回一个HTML文件。

静态网站:直接返回HTML文件给用户。

动态网站:用户请求->执行Python程序(与数据库进行交互)->执行结果->返回HTML给用户。

(文/开源中国)    

pip3 install tornado

源码安装
https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz

采用mvc设计web应用:

m: model模型,存储web应用数据的代码

v: view视图,格式化和显示web应用的用户界面的代码

c: controller控制器,将web应用粘合在一起并提供业务逻辑的代码。

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

CGI:

common gateway interface

可以让一个客户端,从网页浏览器向服务器请求数据。这是描述客户端和服务器程序之间传输数据的一个标准。

图片 1

应用于web的编程语言:

PHP

ASP/ASP.net

JSP

Python。

2、web应用的结构

一、最基本tornado使用

web应用结构概览:

前端程序:HTML、CSS、js(混合使用)

后台程序:Python、PHP、JSP(只有一种就可以)

数据库:MySQL(关系型数据库)、mongodb(非关系型数据库)。

前端:

HTML  超文本标记语言,不是被执行,而是被渲染

CSS 层叠样式表,规定样式

js  和JAVA并没有联系。

数据库及静态存储:

MySQL

sqlite

mongodb。

3、HTML和JavaScript的简单介绍

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

application = tornado.web.Application([
    (r"/index", MainHandler),
])


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

目标:

使用HTML语言和js语言,编写程序,使其可以完成两个整数的加法。并将结果输出在HTML页面中。

图片 2

html页面

图片 3

js文件

上述代码执行过程:

二、用Python进行CGI编程

1、CGI介绍

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

CGI是运行在服务器上的程序,提供同客户端HTML页面的接口。它的全称是 common gateway interface(通用网关接口)

异步非阻塞实例

CGI程序可以是Python脚本,Perl脚本,c或者c++程序等。换句话说,CGI程序是不限定适用语言的。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop
import tornado.web
from tornado import httpclient
from tornado.web import asynchronous
from tornado import gen

import uimodules as md
import uimethods as mt

class MainHandler(tornado.web.RequestHandler):
        @asynchronous
        @gen.coroutine
        def get(self):
            print 'start get '
            http = httpclient.AsyncHTTPClient()
            http.fetch("http://127.0.0.1:8008/post/", self.callback)
            self.write('end')

        def callback(self, response):
            print response.body

settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/static/',
    'ui_methods': mt,
    'ui_modules': md,
}

application = tornado.web.Application([
    (r"/index", MainHandler),
], **settings)


if __name__ == "__main__":
    application.listen(8009)
    tornado.ioloop.IOLoop.instance().start()

历史:

1993年由ncsa开发(跟我一样大,哈哈)

1997年制定了CGI1.1规范。

二、tornado的路由系统

CGI工作原理:

浏览器访问URL并连接到web服务器

web服务器接收到请求信息后会解析URL,并查找访问的文件在服务器上是否存在,如果存在就返回文件的内容,否则返回错误信息

浏览器从服务器上接收信息,并显示接收的文件或者错误信息。

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

缺点及改进:

每次的CGI请求都要求生成一个程序的副本来进行

fastcgi-先编译

mod_perl-将解释器作为模块集成在web服务器中

使用c等编译语言。

tornado支持原生路由

CGI中的环境变量:

CGI程序都接收一些环境变量,这些环境变量非常有用,例如:

content_type:指示传递来信息的类型

http_cookie:客户机的cookie内容

remote_addr:发送请求的客户机的IP地址

request_method:脚本被调用的方法,如get和post方法。

图片 4

get方法和post方法:

浏览器客户端通过两种方法向服务器传递信息,这两种方法就是get方法和post方法

get方法发送用户信息到服务端,数据信息包含在请求URL上,以?分割。例如:

get请求可被浏览器缓存

get请求会保存在历史记录中

get请求有长度限制

get请求不应用于敏感场合

get请求只应当用于取回数据

使用post方法向服务器传递数据是更安全可靠的,像一些敏感数据如用户密码等需要使用post传输

post请求会包含在http请求的头中

post请求不会显示在URL中。

2、Python中的web服务器

三、模板的引擎

Python自带的web服务器:

Python自带的包可以建立简单的web服务器

basehttpserver:提供基本的web服务器和处理类

simplehttpserver:包含执行get请求的simpleHttprequestHandler类

CGIHttpServer:包含处理post请求和执行的CGIhttprequesthandler类。

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

示例:

图片 5

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

使用Apache:

Apache是Apache软件基金会的一个开源网页服务器软件。同样由ncsa开发。

Apache可以通过简单的API扩充将Python解释器编译到服务器中。

市场占有率达到53.67%(2009 netcraft)

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

第一个CGI程序:

创建目录

图片 6

编写CGI程序

图片 7

开启服务器

图片 8

使用浏览器访问

图片 9

3、实战:用CGI编写加法器

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

目标:

使用Python语言,使用get请求和post请求,配合HTML和js程序,完成之前的加法器。并开启服务器,使得其他人可以进行访问。

图片 10

创建模板文件

图片 11

主文件

图片 12

运行效果

1、基本的使用

三、Python web框架开发

1、web开发框架介绍

图片 13图片 14

何谓框架?

软件框架,指的是为了实现某个业界标准或者完成特别基本任务的软件组件规范,也指为了实现某个软件组建规范时,提供规范所要求之基础功能的软件产品。

框架的功能类似于基础设施,提供并实现最为基础的软件架构和体系。我们通常依据特定的框架实现更为复杂的业务逻辑。

框架,就是程序的骨架

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

使用框架的好处

可重用

成熟,稳健

可扩展性良好

提高开发速度

app.py

什么是web框架

web应用框架是支持动态网站、网络应用程序的软件框架。

web框架的工作方式:接收http请求并处理,分派代码,产生HTML,创建http响应。

web框架通常包含了:URL路由,数据库管理,模板引擎等。

图片 15图片 16

使用web框架的优缺点

着重业务逻辑

高度可重用

选对框架很重要

 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>

web框架中的概念

mvc

orm  对象关系映射  将一个类映射成数据库中的一张表

URL route  将访问的地址映射到相应的函数中

template  模板  将数据可以传递到HTML代码中进行渲染

2、Python中的web框架

sudo pip install flask

3、实战:使用flask编写加法器

index.html

目标

使用Python语言和flask框架,配合HTML和js程序,完成之前的加法器。并开启服务器,使得其他人可以进行访问。

图片 17

图片 18

图片 19图片 20

四、flask表单开发

1、表单介绍

 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 的別名

何谓表单?

表单是HTML页面中负责数据采集功能的部件。它往往由三部分组成,即表单标签、表单域、表单按钮。我们可以通过表单,将用户输入的数据提交给服务端,并交由服务端进行处理。

其他方法

表单标签

用于声明表单的范围,位于表单标签中的元素将被提交

语法:<form></form>

属性:method, enctype, action…

2、母版

表单域

表单域包含了文本框,密码框等多种类型

语法:<input ……/>

属性:tyep, name, value…

图片 21图片 22

表单域的种类

文本框 <… type = text>

密码框  <… type = password>

文本区域  <… type = textarea>

文件上传框  <… type = file>

单选框  <… type = radio>

复选框  <… type = checkbox>

……

 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语言,尝试编写一个含有多种元素的表单。并进行简单的排版。同时,尝试通过JavaScript取得页面元素的值。

图片 23

图片 24

2、表单的提交方式

图片 25图片 26

get和post

get方式通过URL提交数据,数据在URL中可以看到

post方式,数据放置在HTML header中提交

例如:get方式:www.baidu.com/?wd=value

post方式:在http头中提交 wd=value

 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 %}

两种提交方式的区别

get请求可以被浏览器缓存

post所请求的URL可以被缓存,但数据不会被缓存

post请求不便于分享

post请求没有限制

get请求的数据暴露在URL中,会带来一些安全问题

index.html

get方式的使用场合

单纯的请求数据,不进行其他操作

表单数据较短,不超过1024个字符

对安全性要求一般的场合

3、导入

post方式适用的场合

数据不仅仅用于请求,例如,需要将数据插入数据库内

表单数据过长时

要传送的数据不是ASCII编码

3、实战:用flask开发用户管理(一)

图片 27图片 28

目标

使用flask框架,配合HTML和JavaScript程序,编写一个页面。该页面判断用户提交的用户名和密码是否分别为dong和123456,如果是则跳转到百度的首页,否则返回错误信息。

图片 29

使用wetforms包来做输入的判断

图片 30

模板里更换表单域

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

sudo pip install Flask-wtf安装wtf包。但是由于我安装了多个版本的Python,pycharm一直运行不了,这时可以通过指定Python版本来解决。

图片 31

pycharm指定Python运行版本

heaher.html

五、Python的数据库连接

1、数据库基础

图片 32图片 33

何谓数据库?

数据库,可以理解为电子化的文件柜。用户可以对文件柜中的数据进行增、删、改、查的操作。

数据库管理系统,是为管理数据库而设计的电脑软件系统。一般会具有存储、截取、安全保障、备份等基础功能。

 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>

数据库的分类

关系型数据库   数据以关系型存放,像一个表格

MySQL、sqlite、postgresql、Oracle、Microsoft Access …

非关系型数据库

bigtable、mongodb、leveldb …

index.html

为什么要使用数据库管理系统(dbms)

减少记录编档的时间

减少记录检索的时间

灵活的查找序列

灵活的输出格式

多个用户同时访问记录

4、自定义UIMethod以UIModule

演示:在Mac上安装MySQL数据库和相关的管理工具

需要安装的软件:

MySQL

workbench – 图形化管理界面

我没有能用成功就在pycharm专业版上用的sqlite数据库。

2、数据库查询语言sql

定义

何谓sql?

结构化查询语言,是一种用语数据库中的标准数据查询语言。由IBM最早用在其开发的数据库系统中。1986年10月,成为关系型数据库管理系统的标准语言。不同数据库系统之间的sql不能完全互相通用。

sql可以完成数据定义、数据操作、数据控制的作用。

图片 34图片 35

sql语言实例

查询 

select Column1 … from tbl where Column = value order
by id desc

插入

insert into tbl (Column1, Column2 …) values (value1, value2)

删除

delete from tbl where Column = value

修改

update tbl set Column = value where Column = value

图片 36

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

uimmethods.py

图片 37图片 38

 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

注册

图片 39图片 40

 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 
29 if __name__ == "__main__":
30     application.listen(8009)
31     tornado.ioloop.IOLoop.instance().start()

View Code

使用

图片 41图片 42

 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>

View Code

四、静态文件

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

图片 43图片 44

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

app.py

图片 45图片 46

 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

静态文件缓存的实现

图片 47图片 48

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

View Code

五、cookie

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

1、基本操作

图片 49图片 50

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

View Code

2、加密cookie(签名)

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

图片 51图片 52

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

View Code

内部算法

图片 53图片 54

  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"2",
 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"0"):
 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 
 92 def _decode_fields_v2(value):
 93     def _consume_field(s):
 94         length, _, rest = s.partition(b':')
 95         n = int(length)
 96         field_value = rest[:n]
 97         # In python 3, indexing bytes returns small integers; we must
 98         # use a slice to get a byte string as in python 2.
 99         if rest[n:n + 1] != b'|':
100             raise ValueError("malformed v2 signed value field")
101         rest = rest[n + 1:]
102         return field_value, rest
103 
104     rest = value[2:]  # remove version number
105     key_version, rest = _consume_field(rest)
106     timestamp, rest = _consume_field(rest)
107     name_field, rest = _consume_field(rest)
108     value_field, passed_sig = _consume_field(rest)
109     return int(key_version), timestamp, name_field, value_field, passed_sig
110 
111 
112 def _decode_signed_value_v2(secret, name, value, max_age_days, clock):
113     try:
114         key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value)
115     except ValueError:
116         return None
117     signed_string = value[:-len(passed_sig)]
118 
119     if isinstance(secret, dict):
120         try:
121             secret = secret[key_version]
122         except KeyError:
123             return None
124 
125     expected_sig = _create_signature_v2(secret, signed_string)
126     if not _time_independent_equals(passed_sig, expected_sig):
127         return None
128     if name_field != utf8(name):
129         return None
130     timestamp = int(timestamp)
131     if timestamp < clock() - max_age_days * 86400:
132         # The signature has expired.
133         return None
134     try:
135         return base64.b64decode(value_field)
136     except Exception:
137         return None
138 
139 
140 def get_signature_key_version(value):
141     value = utf8(value)
142     version = _get_version(value)
143     if version < 2:
144         return None
145     try:
146         key_version, _, _, _, _ = _decode_fields_v2(value)
147     except ValueError:
148         return None
149 
150     return key_version

View Code

签名Cookie的本质是:

写cookie过程:

将值进行base64加密
对除值以外的内容进行签名,哈希算法(无法逆向解析)
拼接 签名 + 加密值
读cookie过程:

读取 签名 + 加密值
对签名进行验证
base64解密,获取值内容
注:许多API验证机制和安全cookie的实现机制相同。

图片 55图片 56

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

基于cookie实现用户验证

图片 57图片 58

 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  
20  
21 class LoginHandler(tornado.web.RequestHandler):
22     def get(self):
23         self.current_user()
24  
25         self.render('login.html', **{'status': ''})
26  
27     def post(self, *args, **kwargs):
28  
29         username = self.get_argument('name')
30         password = self.get_argument('pwd')
31         if username == 'wupeiqi' and password == '123':
32             self.set_secure_cookie('login_user', '呵呵')
33             self.redirect('/')
34         else:
35             self.render('login.html', **{'status': '用户名或密码错误'})
36  
37 settings = {
38     'template_path': 'template',
39     'static_path': 'static',
40     'static_url_prefix': '/static/',
41     'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
42     'login_url': '/login'
43 }
44  
45 application = tornado.web.Application([
46     (r"/index", MainHandler),
47     (r"/login", LoginHandler),
48 ], **settings)
49  
50  
51 if __name__ == "__main__":
52     application.listen(8888)
53     tornado.ioloop.IOLoop.instance().start()

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

3、JavaScript操作Cookie

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

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

对于参数:

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

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

六、csrf

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

图片 59图片 60

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

配置

图片 61图片 62

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>

普通表单

图片 63图片 64

 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表单上传

图片 65图片 66

 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>

View Code

图片 67图片 68

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

app.py

ajax上传

图片 69图片 70

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

HTML – XMLHttpRequest

图片 71图片 72

 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

图片 73图片 74

 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

图片 75图片 76

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

python

图片 77图片 78

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

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

八、验证码

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

安装图像处理模块

pip3 install pillow

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

九、异步非阻塞

1、基本使用

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

class AsyncHandler(tornado.web.RequestHandler):

    @gen.coroutine
    def get(self):
        future = Future()
        future.add_done_callback(self.doing)
        yield future
        # 或
        # tornado.ioloop.IOLoop.current().add_future(future,self.doing)
        # yield future

    def doing(self,*args, **kwargs):
        self.write('async')
        self.finish()

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

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

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

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

图片 79图片 80

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)

同步阻塞

图片 81图片 82

 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 
 9     def doing(self, *args, **kwargs):
10         self.write('async')
11         self.finish()

异步非阻塞

3、httpclient类库

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

图片 83图片 84

 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  
10     def endding(self, response):
11         print(len(response.body))
12         self.write('ok')
13         self.finish()

View Code

十、session

1、面向对象基础

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

图片 85图片 86

 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    
16    
17 obj = Foo()
18 result = obj['k1']
19 #obj['k2'] = 'wupeiqi'
20 #del obj['k1']

View Code

2、Tornado扩展

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

图片 87图片 88

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

View Code

3、session

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

图片 89图片 90

 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 
11 class SessionFactory:
12 
13     @staticmethod
14     def get_session_obj(handler):
15         obj = None
16 
17         if config.SESSION_TYPE == "cache":
18             obj = CacheSession(handler)
19         elif config.SESSION_TYPE == "memcached":
20             obj = MemcachedSession(handler)
21         elif config.SESSION_TYPE == "redis":
22             obj = RedisSession(handler)
23         return obj
24 
25 
26 class CacheSession:
27     session_container = {}
28     session_id = "__sessionId__"
29 
30     def __init__(self, handler):
31         self.handler = handler
32         client_random_str = handler.get_cookie(CacheSession.session_id, None)
33         if client_random_str and client_random_str in CacheSession.session_container:
34             self.random_str = client_random_str
35         else:
36             self.random_str = create_session_id()
37             CacheSession.session_container[self.random_str] = {}
38 
39         expires_time = time.time() + config.SESSION_EXPIRES
40         handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time)
41 
42     def __getitem__(self, key):
43         ret = CacheSession.session_container[self.random_str].get(key, None)
44         return ret
45 
46     def __setitem__(self, key, value):
47         CacheSession.session_container[self.random_str][key] = value
48 
49     def __delitem__(self, key):
50         if key in CacheSession.session_container[self.random_str]:
51             del CacheSession.session_container[self.random_str][key]
52 
53 
54 class RedisSession:
55     def __init__(self, handler):
56         pass
57 
58 
59 class MemcachedSession:
60     def __init__(self, handler):
61         pass

自定义session

4、分布式Session

图片 91图片 92

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

一致性哈希

图片 93图片 94

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

session

十一、表单的验证

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

图片 95图片 96

 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

图片 97图片 98

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

app.py

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

图片 99图片 100

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

View Code