Tornado 的性能是相当优异的,因为它试图解决一个被称之为“C10k”问题,就是处理大于或等于一万的并发。一万呀,这可是不小的量

条件:处理器为 AMD Opteron, 主频 2.4GHz, 4 核

安装 Tornado

pip install tornado

异步非阻塞示例

#!/usr/bin/env python#-*- coding:utf-8 -*-#!/usr/bin/env python#-*- coding:utf-8 -*-importtornado.ioloopimporttornado.webfrom tornado importhttpclientfrom tornado.web importasynchronousfrom tornado importgenimportuimodules as mdimportuimethods as mtclassMainHandler(tornado.web.RequestHandler):@asynchronous@gen.coroutinedefget(self):print 'start get'http=httpclient.AsyncHTTPClient()http.fetch("http://127.0.0.1:8008/post/", self.callback)self.write('end')defcallback(self, response):printresponse.bodysettings={'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()异步非阻塞示例异步非阻塞示例

View Code

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

一、模板引擎

在模板中默认提供了一些函数、字段、类以供模板使用:escape: tornado.escape.xhtml_escape 的別名
xhtml_escape: tornado.escape.xhtml_escape 的別名
url_escape: tornado.escape.url_escape 的別名
json_encode: tornado.escape.json_encode 的別名
squeeze: tornado.escape.squeeze 的別名
linkify: tornado.escape.linkify 的別名
datetime: Python 的 datetime 模组
handler: 当前的 RequestHandler 对象
request: handler.request 的別名
current_user: handler.current_user 的別名
locale: handler.locale 的別名
_: handler.locale.translate 的別名
static_url: for handler.static_url 的別名
xsrf_form_html: handler.xsrf_form_html 的別名其他方法

1、母版

<!DOCTYPE html>
<html>
<head><metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"/><title>老男孩</title><linkhref="{{static_url("css/common.css")}}" rel="stylesheet" />{% block CSS %}{% end %}</head>
<body><divclass="pg-header"></div>{% block RenderBody %}{% end %}<scriptsrc="{{static_url("js/jquery-1.8.2.min.js")}}"></script>{% block JavaScript %}{% end %}</body>
</html>

layout.html

{% extends 'layout.html'%}
{% block CSS %}<linkhref="{{static_url("css/index.css")}}" rel="stylesheet" />{% end %}{% block RenderBody %}<h1>Index</h1><ul>{%  for item in li %}<li>{{item}}</li>{% end %}</ul>{% end %}{% block JavaScript %}{% end %}

index.html

2、导入

<div><ul><li>1024</li><li>42区</li></ul>
</div>

<!DOCTYPE html>
<html>
<head><metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"/><title>老男孩</title><linkhref="{{static_url("css/common.css")}}" rel="stylesheet" />
</head>
<body><divclass="pg-header">{% include 'header.html' %}</div><scriptsrc="{{static_url("js/jquery-1.8.2.min.js")}}"></script></body>
</html>

index

3、自定义UIMethod以UIModule

a. 定义

# uimethods.pydef tab(self):return 'UIMethod'

#!/usr/bin/env python#-*- coding:utf-8 -*-
from tornado.web importUIModulefrom tornado importescapeclasscustom(UIModule):def render(self, *args, **kwargs):return escape.xhtml_escape('<h1>wupeiqi</h1>')#return escape.xhtml_escape('<h1>wupeiqi</h1>')

uimodules.py

b. 注册

#!/usr/bin/env python#-*- coding:utf-8 -*-#!/usr/bin/env python#-*- coding:utf-8 -*-importtornado.ioloopimporttornado.webfrom tornado.escape importlinkifyimportuimodules as mdimportuimethods as mtclassMainHandler(tornado.web.RequestHandler):defget(self):self.render('index.html')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()

View Code

c. 使用

<!DOCTYPE html>
<html>
<headlang="en"><metacharset="UTF-8"><title></title><linkhref="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body><h1>hello</h1>{% module custom(123) %}{{ tab() }}</body>

二、静态文件

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

settings = {'template_path': 'template','static_path': 'static','static_url_prefix': '/static/',
}
<link href="{{static_url("commons.css")}}" rel="stylesheet" />

静态文件缓存的实现

defget_content_version(cls, abspath):"""Returns a version string for the resource at the given path.This class method may be overridden by subclasses.  Thedefault implementation is a hash of the file's contents... versionadded:: 3.1"""data=cls.get_content(abspath)hasher=hashlib.md5()ifisinstance(data, bytes):hasher.update(data)else:for chunk indata:hasher.update(chunk)return hasher.hexdigest()

View Code

三、cookie

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

1、基本操作

classMainHandler(tornado.web.RequestHandler):defget(self):if not self.get_cookie("mycookie"):self.set_cookie("mycookie", "myvalue")self.write("Your cookie was not set yet!")else:self.write("Your cookie was set!")

2、加密cookie(签名)

Cookie 很容易被恶意的客户端伪造。你需要对 cookie 作签名以防止伪造。Tornado 通过 set_secure_cookie 和 get_secure_cookie 方法直接支持了这种功能

classMainHandler(tornado.web.RequestHandler):defget(self):if not self.get_secure_cookie("mycookie"):self.set_secure_cookie("mycookie", "myvalue")self.write("Your cookie was not set yet!")else:self.write("Your cookie was set!")application=tornado.web.Application([(r"/", MainHandler),
], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")

View Code

def _create_signature_v1(secret, *parts):hash= hmac.new(utf8(secret), digestmod=hashlib.sha1)for part inparts:hash.update(utf8(part))returnutf8(hash.hexdigest())#加密
def_create_signature_v2(secret, s):hash= hmac.new(utf8(secret), digestmod=hashlib.sha256)hash.update(utf8(s))returnutf8(hash.hexdigest())def create_signed_value(secret, name, value, version=None, clock=None,key_version=None):if version isNone:version=DEFAULT_SIGNED_VALUE_VERSIONif clock isNone:clock=time.timetimestamp=utf8(str(int(clock())))value=base64.b64encode(utf8(value))if version == 1:signature=_create_signature_v1(secret, name, value, timestamp)value= b"|".join([value, timestamp, signature])returnvalueelif version == 2:#The v2 format consists of a version number and a series of#length-prefixed fields "%d:%s", the last of which is a#signature, all separated by pipes.  All numbers are in#decimal format with no leading zeros.  The signature is an#HMAC-SHA256 of the whole string up to that point, including#the final pipe.#        #The fields are:#- format version (i.e. 2; no length prefix)#- key version (integer, default is 0)#- timestamp (integer seconds since epoch)#- name (not encoded; assumed to be ~alphanumeric)#- value (base64-encoded)#- signature (hex-encoded; no length prefix)defformat_field(s):return utf8("%d:" % len(s)) +utf8(s)to_sign= b"|".join([b"2",format_field(str(key_versionor0)),format_field(timestamp),format_field(name),format_field(value),b''])ifisinstance(secret, dict):assert key_version is not None, 'Key version must be set when sign key dict is used'assert version >= 2, 'Version must be at least 2 for key version support'secret=secret[key_version]signature=_create_signature_v2(secret, to_sign)return to_sign +signatureelse:raise ValueError("Unsupported version %d" %version)#解密
def_decode_signed_value_v1(secret, name, value, max_age_days, clock):parts= utf8(value).split(b"|")if len(parts) != 3:returnNonesignature= _create_signature_v1(secret, name, parts[0], parts[1])if not _time_independent_equals(parts[2], signature):gen_log.warning("Invalid cookie signature %r", value)returnNonetimestamp= int(parts[1])if timestamp < clock() - max_age_days * 86400:gen_log.warning("Expired cookie %r", value)returnNoneif timestamp > clock() + 31 * 86400:#_cookie_signature does not hash a delimiter between the#parts of the cookie, so an attacker could transfer trailing#digits from the payload to the timestamp without altering the#signature.  For backwards compatibility, sanity-check timestamp#here instead of modifying _cookie_signature.gen_log.warning("Cookie timestamp in future; possible tampering %r",value)returnNoneif parts[1].startswith(b"0"):gen_log.warning("Tampered cookie %r", value)returnNonetry:returnbase64.b64decode(parts[0])exceptException:returnNonedef_decode_fields_v2(value):def_consume_field(s):length, _, rest= s.partition(b':')n=int(length)field_value=rest[:n]#In python 3, indexing bytes returns small integers; we must#use a slice to get a byte string as in python 2.if rest[n:n + 1] != b'|':raise ValueError("malformed v2 signed value field")rest= rest[n + 1:]returnfield_value, restrest= value[2:]  #remove version numberkey_version, rest =_consume_field(rest)timestamp, rest=_consume_field(rest)name_field, rest=_consume_field(rest)value_field, passed_sig=_consume_field(rest)returnint(key_version), timestamp, name_field, value_field, passed_sigdef_decode_signed_value_v2(secret, name, value, max_age_days, clock):try:key_version, timestamp, name_field, value_field, passed_sig=_decode_fields_v2(value)exceptValueError:returnNonesigned_string= value[:-len(passed_sig)]ifisinstance(secret, dict):try:secret=secret[key_version]exceptKeyError:returnNoneexpected_sig=_create_signature_v2(secret, signed_string)if not_time_independent_equals(passed_sig, expected_sig):returnNoneif name_field !=utf8(name):returnNonetimestamp=int(timestamp)if timestamp < clock() - max_age_days * 86400:#The signature has expired.returnNonetry:returnbase64.b64decode(value_field)exceptException:returnNonedefget_signature_key_version(value):value=utf8(value)version=_get_version(value)if version < 2:returnNonetry:key_version, _, _, _, _=_decode_fields_v2(value)exceptValueError:returnNonereturn key_version

内部算法

签名Cookie的本质是:

写cookie过程:

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

读cookie过程:

  • 读取 签名 + 加密值
  • 对签名进行验证
  • base64解密,获取值内容

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

#!/usr/bin/env python#-*- coding:utf-8 -*-importtornado.ioloopimporttornado.webclassMainHandler(tornado.web.RequestHandler):defget(self):login_user= self.get_secure_cookie("login_user", None)iflogin_user:self.write(login_user)else:self.redirect('/login')classLoginHandler(tornado.web.RequestHandler):defget(self):self.current_user()self.render('login.html', **{'status': ''})def post(self, *args, **kwargs):username= self.get_argument('name')password= self.get_argument('pwd')if username == 'wupeiqi' and password == '123':self.set_secure_cookie('login_user', '武沛齐')self.redirect('/')else:self.render('login.html', **{'status': '用户名或密码错误'})settings={'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','cookie_secret': 'aiuasdhflashjdfoiuashdfiuh'}application=tornado.web.Application([(r"/index", MainHandler),(r"/login", LoginHandler),
],**settings)if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()

基于Cookie实现用户验证-Demo

#!/usr/bin/env python#-*- coding:utf-8 -*-importtornado.ioloopimporttornado.webclassBaseHandler(tornado.web.RequestHandler):defget_current_user(self):return self.get_secure_cookie("login_user")classMainHandler(BaseHandler):@tornado.web.authenticateddefget(self):login_user=self.current_userself.write(login_user)classLoginHandler(tornado.web.RequestHandler):defget(self):self.current_user()self.render('login.html', **{'status': ''})def post(self, *args, **kwargs):username= self.get_argument('name')password= self.get_argument('pwd')if username == 'wupeiqi' and password == '123':self.set_secure_cookie('login_user', '武沛齐')self.redirect('/')else:self.render('login.html', **{'status': '用户名或密码错误'})settings={'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','cookie_secret': 'aiuasdhflashjdfoiuashdfiuh','login_url': '/login'}application=tornado.web.Application([(r"/index", MainHandler),(r"/login", LoginHandler),
],**settings)if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()

基于签名Cookie实现用户验证-Demo

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

View Code

对于参数:

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

注:jQuery中也有指定的插件 jQuery Cookie 专门用于操作cookie   http://plugins.jquery.com/cookie/

四、CSRF

Tornado中的夸张请求伪造和Django中的相似

settings ={"xsrf_cookies": True,
}
application=tornado.web.Application([(r"/", MainHandler),(r"/login", LoginHandler),
],**settings)

<formaction="/new_message"method="post">{{ xsrf_form_html() }}<inputtype="text"name="message"/><inputtype="submit"value="Post"/>
</form>

普通表单

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

使用 - AJAX

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

五、上传文件

1、Form表单上传

classMainHandler(tornado.web.RequestHandler):defget(self):self.render('index.html')def post(self, *args, **kwargs):file_metas= self.request.files["fff"]#print(file_metas)for meta infile_metas:file_name= meta['filename']with open(file_name,'wb') as up:up.write(meta['body'])

<!DOCTYPE html>
<html>
<head><metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"/><title>上传文件</title>
</head>
<body><formid="my_form"name="form"action="/index"method="POST"enctype="multipart/form-data" ><inputname="fff"id="my_file"type="file" /><inputtype="submit"value="提交"  /></form>
</body>
</html>

HTML

2、AJAX上传

<!DOCTYPE html>
<html>
<headlang="en"><metacharset="UTF-8"><title></title>
</head>
<body><inputtype="file"id="img" /><inputtype="button"onclick="UploadFile();" /><script>functionUploadFile(){varfileObj=document.getElementById("img").files[0];varform= newFormData();form.append("k1","v1");form.append("fff", fileObj);varxhr= newXMLHttpRequest();xhr.open("post",'/index',true);xhr.send(form);}</script>
</body>
</html>

HTML - XMLHttpRequest

<!DOCTYPE html>
<html>
<headlang="en"><metacharset="UTF-8"><title></title>
</head>
<body><inputtype="file"id="img" /><inputtype="button"onclick="UploadFile();" /><script>functionUploadFile(){varfileObj=$("#img")[0].files[0];varform= newFormData();form.append("k1","v1");form.append("fff", fileObj);$.ajax({type:'POST',url:'/index',data: form,processData:false,//tell jQuery not to process the data
contentType:false,//tell jQuery not to set contentType
success:function(arg){console.log(arg);}})}</script>
</body>
</html>

HTML - jQuery

<!DOCTYPE html>
<html>
<headlang="en"><metacharset="UTF-8"><title></title>
</head>
<body><formid="my_form"name="form"action="/index"method="POST"enctype="multipart/form-data" ><divid="main"><inputname="fff"id="my_file"type="file" /><inputtype="button"name="action"value="Upload"onclick="redirect()"/><iframeid='my_iframe'name='my_iframe'src=""class="hide"></iframe></div></form><script>functionredirect(){document.getElementById('my_iframe').onload=Testt;document.getElementById('my_form').target= 'my_iframe';document.getElementById('my_form').submit();}functionTestt(ths){vart=$("#my_iframe").contents().find("body").text();console.log(t);}</script>
</body>
</html>

HTML - iframe

#!/usr/bin/env python#-*- coding:utf-8 -*-importtornado.ioloopimporttornado.webclassMainHandler(tornado.web.RequestHandler):defget(self):self.render('index.html')def post(self, *args, **kwargs):file_metas= self.request.files["fff"]#print(file_metas)for meta infile_metas:file_name= meta['filename']with open(file_name,'wb') as up:up.write(meta['body'])settings={'template_path': 'template',
}application=tornado.web.Application([(r"/index", MainHandler),
],**settings)if __name__ == "__main__":application.listen(8000)tornado.ioloop.IOLoop.instance().start()

Python

<script type="text/javascript">$(document).ready(function() {$("#formsubmit").click(function() {var iframe = $('<iframe name="postiframe" id="postiframe" style="display: none"></iframe>');$("body").append(iframe);var form = $('#theuploadform');form.attr("action", "/upload.aspx");form.attr("method", "post");form.attr("encoding", "multipart/form-data");form.attr("enctype", "multipart/form-data");form.attr("target", "postiframe");form.attr("file", $('#userfile').val());form.submit();$("#postiframe").load(function() {iframeContents= this.contentWindow.document.body.innerHTML;$("#textarea").html(iframeContents);});return false;});});</script><form id="theuploadform"><input id="userfile" name="userfile" size="50" type="file" /><input id="formsubmit" type="submit" value="Send File" />
</form><div id="textarea">
</div>

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

六、验证码

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

安装图像处理模块:pip3 install pillow

示例截图:

classCheckCodeHandler(BaseRequestHandler):def get(self, *args, **kwargs):stream=io.BytesIO()img, code=check_code.create_validate_code()img.save(stream,"png")self.session["CheckCode"] =codeself.write(stream.getvalue())

自定义Web组件

一、Session

1、面向对象基础

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

#!/usr/bin/env python#-*- coding:utf-8 -*-classFoo(object):def __getitem__(self, key):print  '__getitem__',keydef __setitem__(self, key, value):print '__setitem__',key,valuedef __delitem__(self, key):print '__delitem__',keyobj=Foo()
result= obj['k1']#obj['k2'] = 'wupeiqi'#del obj['k1']

View Code

2、Tornado扩展

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

classBaseHandler(tornado.web.RequestHandler):definitialize(self):self.xxoo= "wupeiqi"classMainHandler(BaseHandler):defget(self):print(self.xxoo)self.write('index')classIndexHandler(BaseHandler):defget(self):print(self.xxoo)self.write('index')

View Code

3、session

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

#!/usr/bin/env python#-*- coding:utf-8 -*-importtornado.ioloopimporttornado.webfrom hashlib importsha1importos, timesession_container={}create_session_id= lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()classSession(object):session_id= "__sessionId__"def __init__(self, request):session_value=request.get_cookie(Session.session_id)if notsession_value:self._id=create_session_id()else:self._id=session_valuerequest.set_cookie(Session.session_id, self._id)def __getitem__(self, key):returnsession_container[self._id][key]def __setitem__(self, key, value):ifsession_container.has_key(self._id):session_container[self._id][key]=valueelse:session_container[self._id]={key: value}def __delitem__(self, key):delsession_container[self._id][key]classBaseHandler(tornado.web.RequestHandler):definitialize(self):#my_session['k1']访问 __getitem__ 方法self.my_session =Session(self)classMainHandler(BaseHandler):defget(self):print self.my_session['c_user']print self.my_session['c_card']self.write('index')classLoginHandler(BaseHandler):defget(self):self.render('login.html', **{'status': ''})def post(self, *args, **kwargs):username= self.get_argument('name')password= self.get_argument('pwd')if username == 'wupeiqi' and password == '123':self.my_session['c_user'] = 'wupeiqi'self.my_session['c_card'] = '12312312309823012'self.redirect('/index')else:self.render('login.html', **{'status': '用户名或密码错误'})settings={'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','cookie_secret': 'aiuasdhflashjdfoiuashdfiuh','login_url': '/login'}application=tornado.web.Application([(r"/index", MainHandler),(r"/login", LoginHandler),
],**settings)if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()自定义Session自定义Session

View Code

4、分布式Session

#!/usr/bin/env python#coding:utf-8importsysimportmathfrom bisect importbisectif sys.version_info >= (2, 5):importhashlibmd5_constructor=hashlib.md5else:importmd5md5_constructor=md5.newclassHashRing(object):"""一致性哈希"""def __init__(self,nodes):'''初始化nodes : 初始化的节点,其中包含节点已经节点对应的权重默认每一个节点有32个虚拟节点对于权重,通过多创建虚拟节点来实现如:nodes = [{'host':'127.0.0.1:8000','weight':1},{'host':'127.0.0.1:8001','weight':2},{'host':'127.0.0.1:8002','weight':1},]'''self.ring=dict()self._sorted_keys=[]self.total_weight=0self.__generate_circle(nodes)def __generate_circle(self,nodes):for node_info innodes:self.total_weight+= node_info.get('weight',1)for node_info innodes:weight= node_info.get('weight',1)node= node_info.get('host',None)virtual_node_count= math.floor((32*len(nodes)*weight) /self.total_weight)for i inxrange(0,int(virtual_node_count)):key= self.gen_key_thirty_two( '%s-%s' %(node, i) )if self._sorted_keys.__contains__(key):raise Exception('该节点已经存在.')self.ring[key]=nodeself._sorted_keys.append(key)defadd_node(self,node):'''新建节点node : 要添加的节点,格式为:{'host':'127.0.0.1:8002','weight':1},其中第一个元素表示节点,第二个元素表示该节点的权重。'''node= node.get('host',None)if notnode:raise Exception('节点的地址不能为空.')weight= node.get('weight',1)self.total_weight+=weightnodes_count= len(self._sorted_keys) + 1virtual_node_count= math.floor((32 * nodes_count * weight) /self.total_weight)for i inxrange(0,int(virtual_node_count)):key= self.gen_key_thirty_two( '%s-%s' %(node, i) )if self._sorted_keys.__contains__(key):raise Exception('该节点已经存在.')self.ring[key]=nodeself._sorted_keys.append(key)defremove_node(self,node):'''移除节点node : 要移除的节点 '127.0.0.1:8000''''for key,value inself.ring.items():if value ==node:delself.ring[key]self._sorted_keys.remove(key)defget_node(self,string_key):'''获取 string_key 所在的节点'''pos=self.get_node_pos(string_key)if pos isNone:returnNonereturn self.ring[ self._sorted_keys[pos]].split(':')defget_node_pos(self,string_key):'''获取 string_key 所在的节点的索引'''if notself.ring:returnNonekey=self.gen_key_thirty_two(string_key)nodes=self._sorted_keyspos=bisect(nodes, key)returnposdefgen_key_thirty_two(self, key):m=md5_constructor()m.update(key)return long(m.hexdigest(), 16)defgen_key_sixteen(self,key):b_key= self.__hash_digest(key)return self.__hash_val(b_key, lambdax: x)def __hash_val(self, b_key, entry_fn):return (( b_key[entry_fn(3)] << 24)|(b_key[entry_fn(2)] << 16)|(b_key[entry_fn(1)] << 8)|b_key[entry_fn(0)] )def __hash_digest(self, key):m=md5_constructor()m.update(key)returnmap(ord, m.digest())"""nodes = [{'host':'127.0.0.1:8000','weight':1},{'host':'127.0.0.1:8001','weight':2},{'host':'127.0.0.1:8002','weight':1},
]ring = HashRing(nodes)
result = ring.get_node('98708798709870987098709879087')
print result"""

一致性哈西

from hashlib importsha1importos, timecreate_session_id= lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()classSession(object):session_id= "__sessionId__"def __init__(self, request):session_value=request.get_cookie(Session.session_id)if notsession_value:self._id=create_session_id()else:self._id=session_valuerequest.set_cookie(Session.session_id, self._id)def __getitem__(self, key):#根据 self._id ,在一致性哈西中找到其对应的服务器IP#找到相对应的redis服务器,如: r = redis.StrictRedis(host='localhost', port=6379, db=0)#使用python redis api 链接#获取数据,即:#return self._redis.hget(self._id, name)def __setitem__(self, key, value):#根据 self._id ,在一致性哈西中找到其对应的服务器IP#使用python redis api 链接#设置session#self._redis.hset(self._id, name, value)def __delitem__(self, key):#根据 self._id 找到相对应的redis服务器#使用python redis api 链接#删除,即:return self._redis.hdel(self._id, name)

session

二、表单验证

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

<!DOCTYPE html>
<html>
<headlang="en"><metacharset="UTF-8"><title></title><linkhref="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body><h1>hello</h1><formaction="/index"method="post"><p>hostname: <inputtype="text"name="host" /> </p><p>ip: <inputtype="text"name="ip" /> </p><p>port: <inputtype="text"name="port" /> </p><p>phone: <inputtype="text"name="phone" /> </p><inputtype="submit" /></form>
</body>
</html>

HTML

#!/usr/bin/env python#-*- coding:utf-8 -*-importtornado.ioloopimporttornado.webfrom hashlib importsha1importos, timeimportreclassMainForm(object):def __init__(self):self.host= "(.*)"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}$"self.port= '(\d+)'self.phone= '^1[3|4|5|8][0-9]\d{8}$'defcheck_valid(self, request):form_dict= self.__dict__for key, regular inform_dict.items():post_value=request.get_argument(key)#让提交的数据 和 定义的正则表达式进行匹配ret =re.match(regular, post_value)printkey,ret,post_valueclassMainHandler(tornado.web.RequestHandler):defget(self):self.render('index.html')def post(self, *args, **kwargs):obj=MainForm()result=obj.check_valid(self)self.write('ok')settings={'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','cookie_secret': 'aiuasdhflashjdfoiuashdfiuh','login_url': '/login'}application=tornado.web.Application([(r"/index", MainHandler),
],**settings)if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()

Python

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

#!/usr/bin/env python#-*- coding:utf-8 -*-importtornado.ioloopimporttornado.webimportreclassField(object):def __init__(self, error_msg_dict, required):self.id_valid=Falseself.value=Noneself.error=Noneself.name=Noneself.error_msg=error_msg_dictself.required=requireddefmatch(self, name, value):self.name=nameif notself.required:self.id_valid=Trueself.value=valueelse:if notvalue:if self.error_msg.get('required', None):self.error= self.error_msg['required']else:self.error= "%s is required" %nameelse:ret=re.match(self.REGULAR, value)ifret:self.id_valid=Trueself.value=ret.group()else:if self.error_msg.get('valid', None):self.error= self.error_msg['valid']else:self.error= "%s is invalid" %nameclassIPField(Field):REGULAR= "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"def __init__(self, error_msg_dict=None, required=True):error_msg= {}  #{'required': 'IP不能为空', 'valid': 'IP格式错误'}iferror_msg_dict:error_msg.update(error_msg_dict)super(IPField, self).__init__(error_msg_dict=error_msg, required=required)classIntegerField(Field):REGULAR= "^\d+$"def __init__(self, error_msg_dict=None, required=True):error_msg= {'required': '数字不能为空', 'valid': '数字格式错误'}iferror_msg_dict:error_msg.update(error_msg_dict)super(IntegerField, self).__init__(error_msg_dict=error_msg, required=required)classCheckBoxField(Field):def __init__(self, error_msg_dict=None, required=True):error_msg= {}  #{'required': 'IP不能为空', 'valid': 'IP格式错误'}iferror_msg_dict:error_msg.update(error_msg_dict)super(CheckBoxField, self).__init__(error_msg_dict=error_msg, required=required)defmatch(self, name, value):self.name=nameif notself.required:self.id_valid=Trueself.value=valueelse:if notvalue:if self.error_msg.get('required', None):self.error= self.error_msg['required']else:self.error= "%s is required" %nameelse:ifisinstance(name, list):self.id_valid=Trueself.value=valueelse:if self.error_msg.get('valid', None):self.error= self.error_msg['valid']else:self.error= "%s is invalid" %nameclassFileField(Field):REGULAR= "^(\w+\.pdf)|(\w+\.mp3)|(\w+\.py)$"def __init__(self, error_msg_dict=None, required=True):error_msg= {}  #{'required': '数字不能为空', 'valid': '数字格式错误'}iferror_msg_dict:error_msg.update(error_msg_dict)super(FileField, self).__init__(error_msg_dict=error_msg, required=required)defmatch(self, name, value):self.name=nameself.value=[]if notself.required:self.id_valid=Trueself.value=valueelse:if notvalue:if self.error_msg.get('required', None):self.error= self.error_msg['required']else:self.error= "%s is required" %nameelse:m=re.compile(self.REGULAR)ifisinstance(value, list):for file_name invalue:r=m.match(file_name)ifr:self.value.append(r.group())self.id_valid=Trueelse:self.id_valid=Falseif self.error_msg.get('valid', None):self.error= self.error_msg['valid']else:self.error= "%s is invalid" %namebreakelse:if self.error_msg.get('valid', None):self.error= self.error_msg['valid']else:self.error= "%s is invalid" %namedef save(self, request, upload_path=""):file_metas=request.files[self.name]for meta infile_metas:file_name= meta['filename']with open(file_name,'wb') as up:up.write(meta['body'])classForm(object):def __init__(self):self.value_dict={}self.error_dict={}self.valid_status=Truedef validate(self, request, depth=10, pre_key=""):self.initialize()self.__valid(self, request, depth, pre_key)definitialize(self):passdef __valid(self, form_obj, request, depth, pre_key):"""验证用户表单请求的数据:param form_obj: Form对象(Form派生类的对象):param request: Http请求上下文(用于从请求中获取用户提交的值):param depth: 对Form内容的深度的支持:param pre_key: Html中name属性值的前缀(多层Form时,内部递归时设置,无需理会):return: 是否验证通过,True:验证成功;False:验证失败"""depth-= 1if depth <0:returnNoneform_field_dict= form_obj.__dict__for key, field_obj inform_field_dict.items():printkey,field_objif isinstance(field_obj, Form) orisinstance(field_obj, Field):ifisinstance(field_obj, Form):#获取以key开头的所有的值,以参数的形式传至self.__valid(field_obj, request, depth, key)continueifpre_key:key= "%s.%s" %(pre_key, key)ifisinstance(field_obj, CheckBoxField):post_value=request.get_arguments(key, None)elifisinstance(field_obj, FileField):post_value=[]file_list=request.request.files.get(key, None)for file_item infile_list:post_value.append(file_item['filename'])else:post_value=request.get_argument(key, None)printpost_value#让提交的数据 和 定义的正则表达式进行匹配
field_obj.match(key, post_value)iffield_obj.id_valid:self.value_dict[key]=field_obj.valueelse:self.error_dict[key]=field_obj.errorself.valid_status=FalseclassListForm(object):def __init__(self, form_type):self.form_type=form_typeself.valid_status=Trueself.value_dict={}self.error_dict={}defvalidate(self, request):name_list= request.request.arguments.keys() +request.request.files.keys()index=0flag=FalsewhileTrue:pre_key= "[%d]" %indexfor name inname_list:ifname.startswith(pre_key):flag=Truebreakifflag:form_obj=self.form_type()form_obj.validate(request, depth=10, pre_key="[%d]" %index)ifform_obj.valid_status:self.value_dict[index]=form_obj.value_dictelse:self.error_dict[index]=form_obj.error_dictself.valid_status=Falseelse:breakindex+= 1flag=FalseclassMainForm(Form):def __init__(self):#self.ip = IPField(required=True)#self.port = IntegerField(required=True)#self.new_ip = IPField(required=True)#self.second = SecondForm()self.fff = FileField(required=True)super(MainForm, self).__init__()#
#class SecondForm(Form):#
#def __init__(self):#self.ip = IPField(required=True)#self.new_ip = IPField(required=True)#
#super(SecondForm, self).__init__()classMainHandler(tornado.web.RequestHandler):defget(self):self.render('index.html')def post(self, *args, **kwargs):#for i in  dir(self.request):#print i#print self.request.arguments#print self.request.files#print self.request.query#name_list = self.request.arguments.keys() + self.request.files.keys()#print name_list#list_form = ListForm(MainForm)#list_form.validate(self)#        #print list_form.valid_status#print list_form.value_dict#print list_form.error_dict#obj = MainForm()#obj.validate(self)#        #print "验证结果:", obj.valid_status#print "符合验证结果:", obj.value_dict#print "错误信息:"#for key, item in obj.error_dict.items():#print key,item#print self.get_arguments('favor'),type(self.get_arguments('favor'))#print self.get_argument('favor'),type(self.get_argument('favor'))#print type(self.get_argument('fff')),self.get_argument('fff')#print self.request.files#obj = MainForm()#obj.validate(self)#print obj.valid_status#print obj.value_dict#print obj.error_dict#print self.request,type(self.request)#obj.fff.save(self.request)#from tornado.httputil import HTTPServerRequest#name_list = self.request.arguments.keys() + self.request.files.keys()#print name_list#print self.request.files,type(self.request.files)#print len(self.request.files.get('fff'))#obj = MainForm()#obj.validate(self)#print obj.valid_status#print obj.value_dict#print obj.error_dict#obj.fff.save(self.request)self.write('ok')settings={'template_path': 'template','static_path': 'static','static_url_prefix': '/static/','cookie_secret': 'aiuasdhflashjdfoiuashdfiuh','login_url': '/login'}application=tornado.web.Application([(r"/index", MainHandler),
],**settings)if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()

View Code

转载于:https://www.cnblogs.com/Erick-L/p/6868591.html

Tornado介绍及自定义组件相关推荐

  1. 微信小程序之自定义组件的使用、介绍、案例分享

    微信小程序自定义组件介绍 自定义组件发开文档 类似vue或者react中的自定义组件, 小程序允许我们使用自定义组件的方式来构建页面. 自定义组件的使用 1. 创建组件(js,json,wxml,wx ...

  2. ExtJS(3)- 自定义组件(星级评分)

    今天介绍ExtJS的组件开发,这里以星级评分为示例,首先来看看效果图: 然后是功能操作:鼠标移动到五个星星上面时,会有tooltip显示当前的分值.如图:鼠标悬停在第四颗星星时前四颗星星显示高亮笑脸, ...

  3. 自定义组件 点击空白处隐藏

    代码实现: <template><div><div class="show" v-show="show" v-clickoutsi ...

  4. 【Android 应用开发】 自定义组件 宽高适配方法, 手势监听器操作组件, 回调接口维护策略, 绘制方法分析 -- 基于 WheelView 组件分析自定义组件

    博客地址 : http://blog.csdn.net/shulianghan/article/details/41520569 代码下载 : -- GitHub : https://github.c ...

  5. app vue 真机运行_uni-app黑魔法:小程序自定义组件运行到H5平台

    引言 移动互联网的初期,囿于设备硬件性能限制,流量以原生App为主,iOS.Android是当时两大平台. 随着硬件及OS的更新换代,H5可承载的体验逐步完善,为提高开发效率.节约资源(复用代码)以及 ...

  6. 微信小程序 基础3【组件化开发、自定义组件、全栈开发、使用Express】

    视频地址: https://www.bilibili.com/video/BV1cW411T7t6  [2018]学做小程序- 清华大学 https://www.bilibili.com/video/ ...

  7. form-create教程:给内置组件和自定义组件添加事件

    本文将介绍form-create如何给内置组件和自定义组件添加事件 form-create 是一个可以通过 JSON 生成具有动态渲染.数据收集.验证和提交功能的表单生成器.并且支持生成任何 Vue ...

  8. mysql抽屉图标_React Native自定义组件实现抽屉菜单控件效果

    一.需求分析 原生开发中,自定义View可谓是屡见不鲜的事情,往往系统的控件总不能满足现实的需求.五花八门的产品设计需要我们做出不同的View.关于自定义View的内容网上已经有很多的博文,本篇博客要 ...

  9. vue 自定义组件 v-model

    官方的介绍讲得比较全,这里就省去复制粘贴的步骤了.此处模拟一种非表单元素的 v-model 组件: 类似复选框,在组件里点选不同的选项,然后能跟父组件双向绑定. 1. 首先做好基础的排版及样式 < ...

  10. 如何使用小程序自定义组件功能

    标签: 小程序 component 需求 小程序开发时通过自定义组件将频繁使用的模块抽取出来,简化代码; 实现难点 小程序文档相关的说明太过于详细,以至于不能快速上手使用,因此这里从顽意小程序中拿出一 ...

最新文章

  1. C语言入门练习 - 第二期 判断语句与循环语句(题解)
  2. Linux下Chrome/Chromium窗口边框有白线
  3. UA PHYS515A 电磁理论V 电磁波与辐射11 简单辐射问题 电偶极子的辐射
  4. matlab小技巧与verilog小技巧
  5. jquery 如何保存拖动空间的位置
  6. python polygon函数_Python 人脸识别就多简单,看这个就够了!
  7. Ogitor的编译配置全过程
  8. 系出名门Android(2) - 布局(Layout)和菜单(Menu)
  9. 按住 ctrl 并滚动鼠标滚轮才可缩放地图_Firefox 73 将引入全局缩放功能,在所有网站都可适用...
  10. git切换用户密码_Git 最基本的命令
  11. JavaScript中的原型和继承
  12. 【付费毕设】php mysql社团报名管理系统
  13. golang Windows下编译linux可执行文件
  14. 将两块球形橡皮泥揉在一起,捏成一个正方体。请编程,完成下述功能:从键盘读入2个球形橡皮泥的直径,直径为浮点数;求正方体的边长,并保留两位小数输出;
  15. pycharm 自定义区域折叠代码
  16. k8s之四层负载均衡Service:概念、原理解读
  17. Android 贝塞尔曲线实战之网易云音乐鲸云特效,2021程序员进阶宝典
  18. 夏天CPU温度过高原因及解决办法
  19. RK3588 调试 phy
  20. 14-网关实战:网关层整合 Swagger 聚合API文档

热门文章

  1. Hadoop系列教程:服务器基础环境
  2. 【前端基础进阶】JS原型、原型链、对象详解
  3. 两个简洁的页面:404和Loading
  4. zuul业务检查相关模块
  5. php 计算两个时间相差的天数、小时数、分钟数、秒数详解及实例代码
  6. 怎么用几何画板作一些简单的图形
  7. Gradle Guide
  8. redis单机单实例一键安装脚本
  9. 因为分区表已变,使用再生龙恢复ubuntu系统后无法休眠的解决办法
  10. go 语言markdown 转 html,Golang中国的markdown转HTML怎么实现