web框架 之 Tornado

时间:2022-09-05 13:24:09

初识 Tornado :

tornado web server 是使用python编写出来的一个轻量级、高可伸缩性和非阻塞IO的Web服务器软件,其特点是采用epoll非阻塞IO,相应快速,可处理数千并发连接,特别适用于实时的Web服务。

概述:

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

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

下载安装:

pip3 install tornado

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

快速上手:

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

执行过程:

  • 第一步:执行脚本,监听 8888 端口
  • 第二步:浏览器客户端访问 /index  -->  http://127.0.0.1:8888/index
  • 第三步:服务器接受请求,并交由对应的类处理该请求
  • 第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法
  • 第五步:方法返回值的字符串内容发送浏览器
import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self):
# self.render("s1.html") #render方法,表示会自动找到文件并打开,返回给你
#render找的时候默认从当前的目录下面去找,如果想让其从别的地方找,我们就可以
#为其做一个settings配置(也可以把绝对路径写上去),
self.write("Hello, world")
settings={ #如果想让配置文件生效,需要在下面application后面加一个**settings
"tempalte_path":"xxx", #模板路径的匹配,其中xxx为放HTML的文件夹
"static_path":"xxx" #静态文件的配置(静态文件就是css和JavaScript),其中xxx为存放静态文件的文件夹
} #路由映射(路由系统)
application = tornado.web.Application([
(r"/index", MainHandler), #检测用户的url是否匹配,如果匹配则,执行其后面类中的莫一种方法
#同一个url以不同的方式访问,执行不同的方法
],**settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

具体分析

二、路由系统 (application

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

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

内部在执行的时候执行了两个方法__init__方法和self.add_handlers(".*$", handlers)方法{源码后期解析Tornado时补充}

这个add_handlers默认传输的".*$" 就是www,他内部生成的路由映射的时候相当于(二级域名的方式)下图:

web框架 之 Tornado

我们可以通过application.add_handlers,添加一个“shuaige.com”,他会生成一个类似下面的对应关系shuaige.*

如果匹配的是shuaige他会去"shuaige"里去找对应关系,如果没有匹配默认就去.*,他这个就类似Django中的URL分类!~~

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

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

#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'luotianshuai' import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world") class Shuaige(tornado.web.RedirectHandler):
def get(self):
self.write("This is shuaige web site,hello!") application = tornado.web.Application([
(r"/index", MainHandler),
]) application.add_handlers("shuaige.com",([ #二级路由
(r"/index", Shuaige),
])
) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

模板:

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

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

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

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

settings = {
'template_path':'views', #设置模板路径,HTML文件放置views文件夹中
'static_path':'static', # 设置静态模板路径,css,JS,Jquery等静态文件放置static文件夹中
'static_url_prefix': '/sss/', #导入时候需要加上/sss/,例如<script src="/sss/jquery-1.9.1.min.js"></script> 自己去找该js文件
'cookie_secret': "asdasd", #cookie生成秘钥时候需提前生成随机字符串,需要在这里进行渲染
'xsrf_cokkies':True, #允许CSRF使用
}
application = tornado.web.Application([
(r'/index',IndexHandler), # 路由映射 路由系统
],**settings)

模板路径

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.pg-header{
height: 48px;
background-color: darkcyan;
}
.pg-footer{
height: 100px;
background-color:beige;
}
</style>
</head>
<body>
<div class="pg-header"> </div>
<div class="pg-contet">
{% block body %} {% end %}
</div>
<div class="pg-footer">AAAAAAAAAA</div>
<script src="{{static_url('js/jquery-1.8.2.min.js')}}"></script>
{% block js %} {% end %}
</body>
</html>

母版 layout.html

{% extends '../master/layout.html'%}

{% block body %}
<h1>Index</h1>
{% include "../include/form.html" %}
{% end %} {% block js %} {% end %}

子模板 index.html

模板中for循环的语法

{% extends '../master/layout.html'%}

{% block body %}
<h1>Fuck</h1>
{% include "../include/form.html" %}
{% include "../include/form.html" %}
{% include "../include/form.html" %}
{% include "../include/form.html" %}
{% end %}

fuck.html

<form action="/">
<input type="text"/>
<input type="submit"/>
</form>
<ul>
{% for item in list_info %}
<li>{{item}}</li>
{% end %}
</ul>index

form.html

在模板中默认提供了一些函数、字段、类以供模板使用:
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 的別名

分享一个前后端交互实例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="sss/commons.css" >
</head>
<body>
<h1>提交数据</h1>
<form method="get" action="/index">
<input type="text" name="xxx" />
<input type="submit" value="提交" />
</form>
<h1>展示内容</h1>
<h3>{{npm}}</h3>
<h3>{{ func(npm) }}</h3>
{% module custom(123) %}
<ul>
{% for item in xxxooo %}
{% if item == "alex" %}
<li style="color: red">{{item}}</li>
{% else %}
<li>{{item}}</li>
{% end %}
{% end %}
</ul> <script src="/sss/oldboy.js"></script>
</body>
</html>

index.html

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
import uimethod as mt
import uimodule as md INPUTS_LIST = ['alex']
class MainHandler(tornado.web.RequestHandler):
def get(self):
name = self.get_argument("xxx",None)
if name:
INPUTS_LIST.append(name)
self.render('s1.html',npm="NPM888",xxxooo = INPUTS_LIST)
# def post(self,*args,**kwargs):
# name = self.get_argument("xxx",None)
# INPUTS_LIST.append(name)
# self.render('s1.html',npm="NPM888",xxxooo = INPUTS_LIST) settings={
'template_path':'tpl',
'static_path':'static',
'static_url_prefix':'/sss/',
'ui_methods':mt,
'ui_modules':md
} application=tornado.web.Application([
(r'/index',MainHandler)
],**settings) if __name__ == '__main__':
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

python

模板语言有三类:

1、{{npm}}-------------self.render("s1.html",npm = "NPM888")

2、代码块的方式

{% for item in xxxooo %}     ----   self.render('s1.html',xxxooo = INPUTS_LIST)
    {% if item == "alex" %}
<li style="color: red">{{item}}</li>
{% else %}
<li>{{item}}</li>
{% end %}
{% end %}

3、自定义

def func(self,arg):
return "12345"

uimethods

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

uimodule

cookie 

Cookie是当你浏览某网站时,网站存储在你机器上的一个小文本文件,它记录了你的用户ID,密码、浏览过的网页、停留的时间等信息,当你再次来到该网站时,网站通过读取Cookie,得知你的相关信息,就可以做出相应的动作,如在页面显示欢迎你的标语,或者让你不用输入ID、密码就直接登录等。

#!/usr/bin/env/python
# -*- coding:utf-8 -*- import tornado.web class IndexHandler(tornado.web.RequestHandler):
def get(self):
print(self.cookies) #获取http请求中携带的浏览器中的所有cookie
print(self.get_cookie("k1")) # 获取浏览器中的cooki
self.set_cookie('k1','') #为浏览器设置cookie
self.render('index.html') settings = {
'template_path':'views',
'static_path':'static'
}
application = tornado.web.Application([
(r'/index',IndexHandler), ],**settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

add.py

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <script>
function setCookie(name,value,expires) {
var current_date = new Date();
current_date.setSeconds(current_date.getSeconds()+5);
document.cookie = name + '='+ value +';expires='+ current_date.toUTCString();
}
</script> </body>
</html>

index.html

2、加密cookie(签名)

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

签名Cookie的本质是:

写cookie过程:

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

v1 = base64(v1)

k1 =  v1 | 加密串(md5(v1+时间戳+自定义字符串)) | 时间戳

读cookie过程:

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

web框架 之 Tornado

#!/usr/bin/env/python
# -*- coding:utf-8 -*-
import tornado.web
import hashlib
import time
xin = {} #创建一个空字典
class IndexHandler(tornado.web.RequestHandler):
def get(self):
if self.get_argument('u',None) in['kai','xin']:
obj = hashlib.md5()
obj.update(bytes(str(time.time()),encoding="utf-8"))
random_str = obj.hexdigest()
xin[random_str] = {}
xin[random_str]['k1']=123
xin[random_str]['k2']=self.get_argument('u',None)+'parents'
xin[random_str]['is_login']=True
self.set_cookie('iiii',random_str)
else:
self.write('请先登录') class ManagerHandler(tornado.web.RequestHandler):
def get(self):
random_str = self.get_cookie('iiii')
xin_user_info = xin.get(random_str,None)
if not xin_user_info:
self.redirect('/index')
else:
if xin_user_info.get('is_login',None):
time = "%s - %s " %(xin_user_info.get('k1',""),xin_user_info.get('k2',""))
self.write(time)
else:
self.redirect('/index') settings = {
'template_path':'views',
'static_path':'static'
}
application = tornado.web.Application([
(r'/index',IndexHandler),
(r'/manger',ManagerHandler),
],**settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

基于cookie 实现用户验证

五、Session(依赖于cookie)

由于cookie中需要保存客户的很多信息,而且如果信息很多的话,服务端与客户端交互的时候也浪费流量,所以我们需要用很少的一段字符串来保存很多的信息,这就是我们所要引进的session。

cookie 和session 的区别:

1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗    考虑到安全应当使用session。

3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能    考虑到减轻服务器性能方面,应当使用COOKIE。

4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

5、所以个人建议:    将登陆信息等重要信息存放为SESSION    其他信息如果需要保留,可以放在COOKIE中

web框架 之 Tornado

#!/usr/bin/env/python
# -*- coding:utf-8 -*-
import tornado.web container = {}
# container = {
# # "第一个人的随机字符串":{},
# # "第一个人的随机字符串":{'k1': 111, 'parents': '你'},
# } class Session:
def __init__(self, handler):
self.handler = handler
self.random_str = None def __genarate_random_str(self):
import hashlib
import time
obj = hashlib.md5()
obj.update(bytes(str(time.time()), encoding='utf-8'))
random_str = obj.hexdigest()
return random_str def __setitem__(self, key, value):
# 在container中加入随机字符串
# 定义专属于自己的数据
# 在客户端中写入随机字符串
# 判断,请求的用户是否已有随机字符串
if not self.random_str:
random_str = self.handler.get_cookie('__kakaka__')
if not random_str:
random_str = self.__genarate_random_str()
container[random_str] = {}
else:
# 客户端有随机字符串
if random_str in container.keys():
pass
else:
random_str = self.__genarate_random_str()
container[random_str] = {}
self.random_str = random_str # self.random_str = asdfasdfasdfasdf container[self.random_str][key] = value
self.handler.set_cookie("__kakaka__", self.random_str) def __getitem__(self, key):
# 获取客户端的随机字符串
# 从container中获取专属于我的数据
# 专属信息【key】
random_str = self.handler.get_cookie("__kakaka__")
if not random_str:
return None
# 客户端有随机字符串
user_info_dict = container.get(random_str,None)
if not user_info_dict:
return None
value = user_info_dict.get(key, None)
return value class BaseHandler(tornado.web.RequestHandler):
def initialize(self):
self.session = Session(self) class IndexHandler(BaseHandler):
def get(self):
if self.get_argument('u',None) in ['alex','eric']:
self.session['is_login'] = True
self.session['name'] =self.get_argument('u',None)
print(container)
else:
self.write('请你先登录')
class MangerHandler(BaseHandler):
def get(self):
# s = Session(self)
# val = s.get_value('is_login')
val = self.session['is_login']
if val:
# self.write(s.get_value('name'))
self.write(self.session['name'])
else:
self.write('登录失败') class LoginHandler(BaseHandler):
def get(self,*args,**kwargs):
self.render('login.html',status="")
def post(self, *args, **kwargs):
user = self.get_argument('user',None)
pwd = self.get_argument('pwd',None)
code = self.get_argument('code',None)
check_code = self.session['CheckCode']
if code.upper() == check_code.upper():
self.write('验证码正确')
else:
self.render('login.html',status ='验证码错误') settings = {
'template_path':'views',
'statics_path':'static',
}
application = tornado.web.Application([
(r'/index',IndexHandler),
(r'/manger',MangerHandler),
(r'/login',LoginHandler),
],**settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

利用session实现用户验证

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#!/usr/bin/env python
import tornado.web
from controllers import home settings = {
'template_path': 'views', # 模板路径的配置
'static_path': "static", # 静态文件
"cookie_secrte": 'uiuoajskfjalsdjf',
} # 路由映射,路由系统
application = tornado.web.Application([
(r"/index/(?P<page>\d*)", home.IndexHandler),
], **settings) application.add_handlers('buy.wupeiqi.com$',[
(r"/index/(?P<page>\d*)", buy.IndexHandler),
]) if __name__ == "__main__":
# socket运行起来
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

实例 分页 主模块

#!/usr/bin/env python
# -*- coding:utf-8 -*- class Pagenation:
def __init__(self,current_page,all_item,base_url):
try:
page = int(current_page)
except:
page = 1
if page < 1:
page = 1 all_pager, c = divmod(all_item, 5)
if c > 0:
all_pager += 1 self.current_page = page
self.all_pager = all_pager
self.base_url = base_url @property
def start(self):
return (self.current_page - 1) * 5 @property
def end(self):
return self.current_page * 5 def string_pager(self):
list_page = []
if self.all_pager < 11:
s = 1
t = self.all_pager + 1
else: # 总页数大于11
if self.current_page < 6:
s = 1
t = 12
else:
if (self.current_page + 5) < self.all_pager:
s = self.current_page - 5
t = self.current_page + 5 + 1
else:
s = self.all_pager - 11
t = self.all_pager + 1
# 首页
first = '<a href="/index/1">首页</a>'
list_page.append(first)
# 上一页
# 当前页 page
if self.current_page == 1:
prev = '<a href="javascript:void(0);">上一页</a>'
else:
prev = '<a href="/index/%s">上一页</a>' % (self.current_page - 1,)
list_page.append(prev)
for p in range(s, t): # 1-11
if p == self.current_page:
temp = '<a class="active" href="/index/%s">%s</a>' % (p, p)
else:
temp = '<a href="/index/%s">%s</a>' % (p, p)
list_page.append(temp)
if self.current_page == self.all_pager:
nex = '<a href="javascript:void(0);">下一页</a>'
else:
nex = '<a href="/index/%s">下一页</a>' % (self.current_page + 1,) list_page.append(nex) # 尾页
last = '<a href="/index/%s">尾页</a>' % (self.all_pager,)
list_page.append(last) # 跳转
jump = """<input type='text' /><a onclick="Jump('%s',this);">GO</a>""" % ('/index/')
script = """<script>
function Jump(baseUrl,ths){
var val = ths.previousElementSibling.value;
if(val.trim().length>0){
location.href = baseUrl + val;
}
}
</script>"""
list_page.append(jump)
list_page.append(script)
str_page = "".join(list_page)
return str_page

继承子模块

#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.web
from commons import pager
LIST_INFO = [
{'username': 'alex', "email": "alex3721@163.com"},
]
for i in range(300):
temp = {'username': 'alex'+str(i), "email": str(i) + '123@qq.com'}
LIST_INFO.append(temp) class IndexHandler(tornado.web.RequestHandler): def get(self, page):
obj = pager.Pagenation(page, len(LIST_INFO), '/index/')
current_list = LIST_INFO[obj.start:obj.end]
str_page = obj.string_pager()
self.render('home/index.html', list_info = current_list, current_page = obj.current_page, str_page = str_page) def post(self,page):
user = self.get_argument('username')
email = self.get_argument('email')
temp = {'username': user, 'email': email}
LIST_INFO.append(temp)
self.redirect("/index/"+page)

子模块

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.pager a{
display: inline-block;
padding: 5px;
margin: 3px;
background-color: cadetblue;
}
.pager a.active{
background-color: brown;
color: white;
}
</style>
</head>
<body>
<h1>提交数据</h1>
<form method="post" action="/index/{{current_page}}">
<input name="username" type="text" />
<input name="email" type="text" />
<input type="submit" value="提交" />
</form>
<h1>显示数据</h1>
<table border="">
<thead>
<tr>
<th>用户名</th>
<th>邮箱</th>
</tr>
</thead>
<tbody>
{% for line in list_info %}
<tr>
<!--<td>{{line['username']}}</td>-->
<td>{{ line['username'] }}</td>
<td>{{line['email']}}</td>
</tr>
{% end %}
</tbody>
</table>
<div class="pager">
{% raw str_page %}
</div>
</body>
</html>

index.html

tornado 随机验证码

登陆注册的时候,需要验证码的功能,原理为在后台自动创建一张随机图片,然后通过img标签输出到前端。这里我们需要安装一个pillow的模块,相应的生成随机验证代码文件如下,此外还需要一个字体文件

安装图像处理模块:

pip3 install pillow  
#!/usr/bin/env python
#coding:utf-8 import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper() # 大写字母
_numbers = ''.join(map(str, range(3, 10))) # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) def create_validate_code(size=(120, 30),
chars=init_chars,
img_type="GIF",
mode="RGB",
bg_color=(255, 255, 255),
fg_color=(0, 0, 255),
font_size=18,
font_type="Monaco.ttf",
length=4,
draw_lines=True,
n_line=(1, 2),
draw_points=True,
point_chance = 2):
'''
@todo: 生成验证码图片
@param size: 图片的大小,格式(宽,高),默认为(120, 30)
@param chars: 允许的字符集合,格式字符串
@param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
@param mode: 图片模式,默认为RGB
@param bg_color: 背景颜色,默认为白色
@param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
@param font_size: 验证码字体大小
@param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
@param length: 验证码字符个数
@param draw_lines: 是否划干扰线
@param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
@param draw_points: 是否画干扰点
@param point_chance: 干扰点出现的概率,大小范围[0, 100]
@return: [0]: PIL Image实例
@return: [1]: 验证码图片中的字符串
''' width, height = size # 宽, 高
img = Image.new(mode, size, bg_color) # 创建图形
draw = ImageDraw.Draw(img) # 创建画笔 def get_chars():
'''生成给定长度的字符串,返回列表格式'''
return random.sample(chars, length) def create_lines():
'''绘制干扰线'''
line_num = random.randint(*n_line) # 干扰线条数 for i in range(line_num):
# 起始点
begin = (random.randint(0, size[0]), random.randint(0, size[1]))
#结束点
end = (random.randint(0, size[0]), random.randint(0, size[1]))
draw.line([begin, end], fill=(0, 0, 0)) def create_points():
'''绘制干扰点'''
chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100] for w in range(width):
for h in range(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=(0, 0, 0)) def create_strs():
'''绘制验证码字符'''
c_chars = get_chars()
strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开 font = ImageFont.truetype(font_type, font_size)
font_width, font_height = font.getsize(strs) draw.text(((width - font_width) / 3, (height - font_height) / 3),
strs, font=font, fill=fg_color) return ''.join(c_chars) if draw_lines:
create_lines()
if draw_points:
create_points()
strs = create_strs() # 图形扭曲参数
params = [1 - float(random.randint(1, 2)) / 100,
0,
0,
0,
1 - float(random.randint(1, 10)) / 100,
float(random.randint(1, 2)) / 500,
0.001,
float(random.randint(1, 2)) / 500
]
img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大) return img, strs

check_code.py 随机验证码 模块

#!/usr/bin/env/python
# -*- coding:utf-8 -*-
import tornado.web container = {}
# container = {
# # "第一个人的随机字符串":{},
# # "第一个人的随机字符串":{'k1': 111, 'parents': '你'},
# } class Session:
def __init__(self, handler):
self.handler = handler
self.random_str = None def __genarate_random_str(self):
import hashlib
import time
obj = hashlib.md5()
obj.update(bytes(str(time.time()), encoding='utf-8'))
random_str = obj.hexdigest()
return random_str def __setitem__(self, key, value):
# 在container中加入随机字符串
# 定义专属于自己的数据
# 在客户端中写入随机字符串
# 判断,请求的用户是否已有随机字符串
if not self.random_str:
random_str = self.handler.get_cookie('__kakaka__')
if not random_str:
random_str = self.__genarate_random_str()
container[random_str] = {}
else:
# 客户端有随机字符串
if random_str in container.keys():
pass
else:
random_str = self.__genarate_random_str()
container[random_str] = {}
self.random_str = random_str # self.random_str = asdfasdfasdfasdf container[self.random_str][key] = value
self.handler.set_cookie("__kakaka__", self.random_str) def __getitem__(self, key):
# 获取客户端的随机字符串
# 从container中获取专属于我的数据
# 专属信息【key】
random_str = self.handler.get_cookie("__kakaka__")
if not random_str:
return None
# 客户端有随机字符串
user_info_dict = container.get(random_str,None)
if not user_info_dict:
return None
value = user_info_dict.get(key, None)
return value class BaseHandler(tornado.web.RequestHandler):
def initialize(self):
self.session = Session(self) class IndexHandler(BaseHandler):
def get(self):
if self.get_argument('u',None) in ['alex','eric']:
self.session['is_login'] = True
self.session['name'] =self.get_argument('u',None)
print(container)
else:
self.write('请你先登录')
class MangerHandler(BaseHandler):
def get(self):
# s = Session(self)
# val = s.get_value('is_login')
val = self.session['is_login']
if val:
# self.write(s.get_value('name'))
self.write(self.session['name'])
else:
self.write('登录失败') class LoginHandler(BaseHandler):
def get(self,*args,**kwargs):
self.render('login.html',status="")
def post(self, *args, **kwargs):
user = self.get_argument('user',None)
pwd = self.get_argument('pwd',None)
code = self.get_argument('code',None)
check_code = self.session['CheckCode']
if code.upper() == check_code.upper():
self.write('验证码正确')
else:
self.render('login.html',status ='验证码错误')
class CheckCodeHandler(BaseHandler):
def get(self,*args,**kwargs):
import io
import check_code
mstream = io.BytesIO()
#创建图片,并写入验证码
img, code = check_code.create_validate_code()
#将图片对象写入到mstrem
img.save(mstream,'GIF') self.session['CheckCode']=code
self.write(mstream.getvalue()) class CsrfHandler(BaseHandler):
def get(self,*args,**kwargs):
self.render('csrf.html')
def post(self, *args, **kwargs):
self.write('csrf.post') settings = {
'template_path':'views',
'statics_path':'static',
'xsrf_cookies':True
}
application = tornado.web.Application([
(r'/index',IndexHandler),
(r'/manger',MangerHandler),
(r'/login',LoginHandler),
(r'/check_code',CheckCodeHandler),
(r'/csrf',CsrfHandler)
],**settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

python 主模块

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/csrf" method="post">
{% raw xsrf_form_html() %}
<p><input name = 'user' type="text" placeholder=“用户名/></p>
<p><input name="pwd" type="text" placeholder="密码"></p>
<p>
<input name="code" type="text" placeholder="验证码">
<img src="/check_code" onclick="ChangeCode(); id = imgCode">
</p>
<input type="submit" value="提交"/>
<span style="color: #ac2925"></span>
</form> <script src="/static/jquery-1.12.4.js"></script> <script>
function ChangeCode() {
var code = document.getElementById('imgCode');
code.sre += "?";
} </script>
</body>
</html>

html

Xss跨站脚本攻击

  恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的特殊目的。
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
jump = '''<input type="text"><a onclick = "Jump('%s',this);">GO</a>'''%('/index/')
script = '''
<script>
function Jump(baseUrl,ths){
var val = ths.previousElementSibling.value;
if (val.trim().length > 0){
location.href = baseUrl + val;
}
}
</script>
'''
self.render('index.html',jump=jump,script=script) #传入两个前端代码的字符串

python

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.pager a{
display: inline-block;
padding: 5px;
margin: 3px;
background-color: #00a2ca;
}
.pager a.active{
background-color: #0f0f0f;
color: white;
}
</style>
</head>
<body>
<div class="pager">
{% raw jump %}
{% raw script%}
</div>
</body>
</html>

html

csrf跨站请求伪造
  get请求的时候,会给浏览器发一个id(cookie),浏览器post请求的时候,携带这个id,然后服务端对其做验证,如果没有这个id的话,就禁止浏览器提交内容。下面来看一下在tornado里面怎么设置,首先需要在settings里面配置 'xsrf_cookies': True,如果这样配置的话,浏览器发送post请求的话这样设置之后,Tornado 将拒绝请求参数中不包含正确的_xsrf 值的 post/put/delete 请求,如果没有携带相应的id(session)则会禁止访问。{% raw xsrf_form_html() %}是新增的,目的就在于实现上面所说的授权给前端以合法请求。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/csrf" method="post">
{% raw xsrf_form_html() %}
<input type="button" value="Ajax CSRF" onclick="SubmitCsrf();" />
</form> <script src="/static/jquery-1.12.4.js"></script> <script> function getCookie(name) {
var r = document.cookie.match('\\b'+ name + "=([^:]*)\\b");
return r ? r[1]:undefined;
}
function SubmitCsrf() {
var nid = getCookie("_xsrf");
$.post({
url:'/csrf',
data:{'k1':'v1','_xsrf':nid},
success:function (callback) {
// Ajax请求发送成功有,自动执行
// callback,服务器write的数据 callback=“csrf.post”
console.log(callback);
}
});
}
</script>
</body>
</html>

csrf.html

#!/usr/bin/env/python
# -*- coding:utf-8 -*-
import tornado.web class CsrfHandler(BaseHandler):
def get(self,*args,**kwargs):
self.render('csrf.html')
def post(self, *args, **kwargs):
self.write('csrf.post') settings = {
'template_path':'views',
'statics_path':'static',
'xsrf_cookies':True
}
application = tornado.web.Application([
(r'/csrf',CsrfHandler)
],**settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start(

python

 ajax

为什么使用ajax,局部刷新,减少请求中发送的数据

AJAX,Asynchronous JavaScript and XML (异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案。

  • 异步的JavaScript:
    使用 【JavaScript语言】 以及 相关【浏览器提供类库】 的功能向服务端发送请求,当服务端处理完请求之后,【自动执行某个JavaScript的回调函数】。以上请求和响应的整个过程是【偷偷】进行的,页面上无任何感知。
  • XML
    XML是一种标记语言,是Ajax在和后台交互时传输数据的格式之一,但是现在使用的很少,基本都是使用json来做数据交换

利用AJAX可以做:
1、注册时,输入用户名自动检测用户是否已经存在。
2、登陆时,提示用户名密码错误
3、删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除。

首先来看一下一种用iframe标签模拟ajax请求

#!/usr/bin/env/python
# -*- coding:utf-8 -*- import tornado.web IMG_LIST = [] class PicturesHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.render('iframe.html',img_list = IMG_LIST) def post(self, *args, **kwargs):
print(self.get_argument('user'))
print(self.get_arguments('favor'))
file_maetas = self.request.files['hahaha']
for meta in file_maetas:
file_name = meta['filename']
import os
with open(os.path.join('static','img',file_name),'wb')as up:
up.write(meta["body"])
IMG_LIST.append(file_name)
self.write('{"status":1,"message":"mmm"}') settings = {
'template_path':"views",
'static_path':'static',
}
application = tornado.web.Application([ (r'/iframe',PicturesHandler)
],**settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

iframe模拟ajax app.py

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<p>请输入要加载的地址:<span id="currentTime"></span></p>
<p>
<input id="url" type="text" />
<input type="button" value="刷新" onclick="LoadPage();">
</p>
</div> <div>
<h3>加载页面位置:</h3>
<iframe id="iframePosition" style="width: 100%;height: 500px;"></iframe>
</div> <script type="text/javascript"> window.onload= function(){
var myDate = new Date();
document.getElementById('currentTime').innerText = myDate.getTime(); }; function LoadPage(){
var targetUrl = document.getElementById('url').value;
document.getElementById("iframePosition").src = targetUrl;
} </script>
</body>
</html>

iframe.html

Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE),Ajax首次出现IE5.5中存在(ActiveX控件)

XmlHttpRequest对象介绍

XmlHttpRequest对象的主要方法:

a. void open(String method,String url,Boolen async)
用于创建请求
参数:
method: 请求方式(字符串类型),如:POST、GET、DELETE...
url: 要请求的地址(字符串类型)
async: 是否异步(布尔类型) b. void send(String body)
用于发送请求
参数:
body: 要发送的数据(字符串类型) c. void setRequestHeader(String header,String value)
用于设置请求头
参数:
header: 请求头的key(字符串类型)
vlaue: 请求头的value(字符串类型) d. String getAllResponseHeaders()
获取所有响应头
返回值:
响应头数据(字符串类型) e. String getResponseHeader(String header)
获取响应头中指定header的值
参数:
header: 响应头的key(字符串类型)
返回值:
响应头中指定的header对应的值 f. void abort()
终止请求

XmlHttpRequest对象的主要属性:

a. Number readyState
状态值(整数)
详细:
0-未初始化,尚未调用open()方法;
1-启动,调用了open()方法,未调用send()方法;
2-发送,已经调用了send()方法,未接收到响应;
3-接收,已经接收到部分响应数据;
4-完成,已经接收到全部响应数据; b. Function onreadystatechange
当readyState的值改变时自动触发执行其对应的函数(回调函数) c. String responseText
服务器返回的数据(字符串类型) d. XmlDocument responseXML
服务器返回的数据(Xml对象) e. Number states
状态码(整数),如:200、404... f. String statesText 状态文本(字符串),如:OK、NotFound...

跨浏览器支持

  XmlHttpRequest IE7+, Firefox, Chrome, Opera, etc.

  • ActiveXObject("Microsoft.XMLHTTP") IE6, IE5
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body> <h1>XMLHttpRequest - Ajax请求</h1>
<input type="button" onclick="XmlGetRequest();" value="Get发送请求" />
<input type="button" onclick="XmlPostRequest();" value="Post发送请求" /> <script src="/statics/jquery-1.12.4.js"></script>
<script type="text/javascript"> function GetXHR(){
var xhr = null;
if(XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr; } function XhrPostRequest(){
var xhr = GetXHR();
// 定义回调函数
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
// 已经接收到全部响应数据,执行以下操作
var data = xhr.responseText;
console.log(data);
}
};
// 指定连接方式和地址----文件方式
xhr.open('POST', "/test/", true);
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
// 发送请求
xhr.send('n1=1;n2=2;');
} function XhrGetRequest(){
var xhr = GetXHR();
// 定义回调函数
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
// 已经接收到全部响应数据,执行以下操作
var data = xhr.responseText;
console.log(data);
}
};
// 指定连接方式和地址----文件方式
xhr.open('get', "/test/", true);
// 发送请求
xhr.send();
} </script> </body>
</html>

基于原生ajax

jQuery其实就是一个JavaScript的类库,其将复杂的功能做了上层封装,使得开发者可以在其基础上写更少的代码实现更多的功能。

  • jQuery 不是生产者,而是大自然搬运工。
  • jQuery Ajax本质 XMLHttpRequest 或 ActiveXObject

注:2.+版本不再支持IE9以下的浏览器

 jQuery.get(...)
所有参数:
url: 待载入页面的URL地址
data: 待发送 Key/value 参数。
success: 载入成功时回调函数。
dataType: 返回内容格式,xml, json, script, text, html jQuery.post(...)
所有参数:
url: 待载入页面的URL地址
data: 待发送 Key/value 参数
success: 载入成功时回调函数
dataType: 返回内容格式,xml, json, script, text, html jQuery.getJSON(...)
所有参数:
url: 待载入页面的URL地址
data: 待发送 Key/value 参数。
success: 载入成功时回调函数。 jQuery.getScript(...)
所有参数:
url: 待载入页面的URL地址
data: 待发送 Key/value 参数。
success: 载入成功时回调函数。 jQuery.ajax(...) 部分参数: url:请求地址
type:请求方式,GET、POST(1.9.0之后用method)
headers:请求头
data:要发送的数据
contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
async:是否异步
timeout:设置请求超时时间(毫秒) beforeSend:发送请求前执行的函数(全局)
complete:完成之后执行的回调函数(全局)
success:成功之后执行的回调函数(全局)
error:失败之后执行的回调函数(全局) accepts:通过请求头发送给服务器,告诉服务器当前客户端课接受的数据类型
dataType:将服务器端返回的数据转换成指定类型
"xml": 将服务器端返回的内容转换成xml格式
"text": 将服务器端返回的内容转换成普通文本格式
"html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
"script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
"json": 将服务器端返回的内容转换成相应的JavaScript对象
"jsonp": JSONP 格式
使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数 如果不指定,jQuery 将自动根据HTTP包MIME信息返回相应类型(an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string converters: 转换器,将服务器端的内容根据指定的dataType转换类型,并传值给success回调函数
$.ajax({
accepts: {
mycustomtype: 'application/x-some-custom-type'
}, // Expect a `mycustomtype` back from server
dataType: 'mycustomtype' // Instructions for how to deserialize a `mycustomtype`
converters: {
'text mycustomtype': function(result) {
// Do Stuff
return newresult;
}
},
});

JQuery ajax 方法列表

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p>
<input type="button" onclick="JqSendRequest();" value='Ajax请求' />
</p>
<script type="text/javascript" src="/c/static/jquery-1.9.1.min.js"></script>
<script>
function JqSendRequest(){
$.ajax({
url: "http://c2.com:8000/test/",
type: 'GET',
data:{"k1":"v1"}, //向服务端发送内容,服务端可以通过self.get_argument("k1")获取
dataType: 'text',
success: function(data, statusText, xmlHttpRequest){
console.log(data,statusText);
}
})
}
</script>
</body>
</html>

基于JQuery ajax

四、跨域AJAX

1.什么引起了ajax跨域不能的问题
ajax本身实际上是通过XMLHttpRequest对象来进行数据的交互,而浏览器出于安全考虑,不允许js代码进行跨域操作,所以会警告。

特别的:由于同源策略是浏览器的限制,所以请求的发送和响应是可以进行,只不过浏览器不接受罢了。

浏览器同源策略并不是对所有的请求均制约:

  • 制约: XmlHttpRequest
  • 不叼: img、iframe、script等具有src属性的标签

跨域,跨域名访问,如:http://www.c1.com 域名向 http://www.c2.com域名发送请求。

1、JSONP实现跨域请求(利用script块的特性)

JSONP(JSONP - JSON with Padding是JSON的一种“使用模式”),利用script标签的src属性(浏览器允许script标签跨域)

#!/usr/bin/env/python
# -*- coding:utf-8 -*- import tornado.web class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
def post(self, *args, **kwargs):
self.write('t1.post') settings = {
'template_path':'views',
'static_path':'static',
}
application = tornado.web.Application([
(r'/index',IndexHandler),
],**settings) if __name__ == "__main__":
application.listen(8001)
tornado.ioloop.IOLoop.instance().start()

app1.py

#!/usr/bin/env/python
# -*- coding:utf-8 -*-
import tornado.web
class IndexHandler(tornado.web.RequestHandler):
def get(self):
callback = self.get_argument('callback')
self.write('%s([11,22,33]);'% callback)
# self.write('func([11,22,33])')
def post(self, *args, **kwargs):
self.write('t2.post') settings = {
'template_path':'views',
'static_path':'static',
}
application = tornado.web.Application([
(r'/index',IndexHandler),
],**settings) if __name__ == "__main__":
application.listen(8002)
tornado.ioloop.IOLoop.instance().start()

app2.py

 1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>Title</title>
6 </head>
7 <body>
8 <input type="button" value="Ajax" onclick="DoAjax();">
9 <input type="button" value="JsonpAjax" onclick="JsonpAjax();">
10 <script src="/statics/jquery-1.12.4.js"></script>
11 <script>
12
13 function func(arg) {
14 console.log(arg)
15 }
16 function DoAjax() {
17 $.ajax({
18 url: 'http://w2.com:8002/index',
19 type: 'POST',
20 data: {'k1': 'v1'},
21 success:function (arg) {
22 console.log(arg)
23 }
24 });
25 }
26
27 function JsonpAjax() {
28
29 // var tag = document.createElement("script");
30 // tag.src = 'http://127.0.0.1:8002/index?callback=func';
31 // document.head.appendChild(tag);
32 // document.head.removeChild(tag);
33
34 $.ajax({
35 url:'http://127.0.0.1:8002/index',
36 dataType: 'jsonp',
37 jsonp: 'callback',
38 jsonpCallBack: 'func'
39 })
40 }
41 </script>
42 </body>
43 </html>

index.tml

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" value="Ajax" onclick="DoAjax();"/>
<input type="button" value="JsonpAjaxJX" onclick="JsonpAjaxJX();"/> <script src="/static/jquery-1.12.4.js"></script>
<script src="127.0.0.1:8002/index/static/jquery.cookie.js"></script>
<script>
function func(arg) {
console.log(arg)
}
function DoAjax() {
$.ajax({
url:'http://127.0.0.1:8002/index',
type:'post',
data:{'k1':'v1'},
success:function (arg) {
console.log(arg);
}
})
} function list(dict) {
console.log(dict)
}
function JsonpAjaxJX() {
$.ajax({
url:'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list',
dataType:'jsonp',
jsonpCallBack:'list'
})
}
</script> </body>
</html>

实例:江西卫视节目表

CORS(客户端不变,服务端设置响应头)

随着技术的发展,现在的浏览器可以支持主动设置从而允许跨域请求,即:跨域资源共享(CORS,Cross-Origin Resource Sharing),其本质是设置响应头,使得浏览器允许跨域请求。

* 简单请求 OR 非简单请求

条件:
1、请求方式:HEAD、GET、POST
2、请求头信息:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type 对应的值是以下三个中的任意一个
application/x-www-form-urlencoded
multipart/form-data
text/plain 注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求 * 简单请求和非简单请求的区别? 简单请求:一次请求
非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。 * 关于“预检” - 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
=> 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
Access-Control-Request-Method => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
Access-Control-Request-Headers

a、支持跨域,简单请求(在服务端加响应头,带相应头就能过来)

服务器设置响应头:Access-Control-Allow-Origin = '域名' 或 '*'      *表示所有的域名都可以访问

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body> <p>
<input type="submit" onclick="XmlSendRequest();" />
</p> <p>
<input type="submit" onclick="JqSendRequest();" />
</p> <script type="text/javascript" src="jquery-1.12.4.js"></script>
<script>
function XmlSendRequest(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4) {
var result = xhr.responseText;
console.log(result);
}
};
xhr.open('GET', "http://c2.com:8000/test/", true);
xhr.send();
} function JqSendRequest(){
$.ajax({
url: "http://c2.com:8000/test/",
type: 'GET',
dataType: 'text',
success: function(data, statusText, xmlHttpRequest){
console.log(data);
}
})
}
</script>
</body>
</html>

html

class MainHandler(tornado.web.RequestHandler):
def get(self):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.write('{"status": true, "data": "seven"}')

python

b、支持跨域,复杂请求

由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。

  • “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
  • “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
  • “预检”缓存时间,服务器设置响应头:Access-Control-Max-Age
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body> <p>
<input type="submit" onclick="XmlSendRequest();" />
</p> <p>
<input type="submit" onclick="JqSendRequest();" />
</p> <script type="text/javascript" src="jquery-1.12.4.js"></script>
<script>
function XmlSendRequest(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4) {
var result = xhr.responseText;
console.log(result);
}
};
xhr.open('PUT', "http://c2.com:8000/test/", true);
xhr.setRequestHeader('k1', 'v1');
xhr.send();
} function JqSendRequest(){
$.ajax({
url: "http://c2.com:8000/test/",
type: 'PUT',
dataType: 'text',
headers: {'k1': 'v1'},
success: function(data, statusText, xmlHttpRequest){
console.log(data);
}
})
}
</script>
</body>
</html>

html

class MainHandler(tornado.web.RequestHandler):

    def put(self):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.write('{"status": true, "data": "seven"}') def options(self, *args, **kwargs):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.set_header('Access-Control-Allow-Headers', "k1,k2")
self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
self.set_header('Access-Control-Max-Age', 10)

python

八、上传文件

form表单上传文件的时候一定要记得加上  enctype="multipart/form-data"

#!/usr/bin/env/python
# -*- coding:utf-8 -*- import tornado.web IMG_LIST = [] # class IndexHandler(tornado.web.RequestHandler):
# def get(self):
# print('asdas')
# self.render('index.html')
# def post(self, *args, **kwargs):
# self.write('{"status":1,"message":"mmm"}') class PicturesHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.render('pictures.html',img_list = IMG_LIST)
# self.render('ajaxsc.html',img_list = IMG_LIST)
# self.render('ifname.html',img_list = IMG_LIST)
# self.render('iframe.html',img_list = IMG_LIST)
#
def post(self, *args, **kwargs):
print(self.get_argument('user'))
print(self.get_arguments('favor'))
file_maetas = self.request.files['hahaha']
for meta in file_maetas:
file_name = meta['filename']
import os
with open(os.path.join('static','img',file_name),'wb')as up:
up.write(meta["body"])
IMG_LIST.append(file_name)
self.write('{"status":1,"message":"mmm"}') settings = {
'template_path':"views",
'static_path':'static', }
application = tornado.web.Application([
(r'/pictures',PicturesHandler)
# (r'/ajaxsc', PicturesHandler)
# (r'/ajaxjq', PicturesHandler)
# (r'/ifname',PicturesHandler)
# (r'/iframe',PicturesHandler)
],**settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

app.py

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for item in img_list %}
<li><img style="width: 200px;height: 200px" src="/static/img/{{item}}"></li>
{% end %}
</ul>
<form action="/pictures" method="post" enctype="multipart/form-data">
<input type="text" name = "user"/>
<h1>性格类型</h1>
<input type="checkbox" name="favor" value=""/>暴虐的;
<input type="checkbox" name="favor" value=""/>温和的;
<input type="checkbox" name="favor" value=""/>傻二的;
<input type="file" name="hahaha"/>
<input type="submit" value="提交"/>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function UploadFile(){
var fileobj = document.getElementById('img').files[0];
var form = new FormData();
form.append('user','uuu');
form.append('favor','');
form.append('hahaha','fileobj');
var xhr = new XMLHttpRequest();
xhr.open('post','/pictures',true);
xhr.send(form);
}
</script> </body>
</html>

pictures.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" value="提交按钮"/>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function UploadFile(){
var fileobj = document.getElementById('img').files[0];
var form = new FormData();
form.append('user','uuu');
form.append('favor','');
form.append('hahaha',fileobj);
var xhr = new XMLHttpRequest();
xhr.open('post','/ajaxsc',true);
xhr.send(form);
}
</script> </body>
</html>

ajaxsc.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" value="提交" />
<script src="/static/jquery-1.12.4.js"></script>
<script>
function UploadFile(){
var fileObj = $("#img")[0].files[0];
var form = new FormData();
form.append("user", "v1");
form.append('favor','');
form.append("hahaha", fileObj);
$.ajax({
type:'POST',
url: '/ajaxjq',
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>

ajaxjq.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.hide{
display: none;
}
</style>
</head>
<body>
<form id="my_form" name="form" action="/ifname" method="POST" enctype="multipart/form-data" >
<div id="main">
<input name="user" type="text" />
<input name="davor" type="text" />
<input name="hahaha" id="my_file" type="file" />
<input type="button" name="action" value="提交" onclick="redirect()"/>
<iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe>
</div>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function redirect(){
document.getElementById('my_iframe').onload = Testt;
//找到id为my_iframe 设置 onload 加载完成执行Testt函数
document.getElementById('my_form').target = 'my_iframe';
//将my_form 目标提交到 id为my_iframe
document.getElementById('my_form').submit(); }
function Testt(ths){
var t = $("#my_iframe").contents().find("body").text();
console.log(t);
}
</script>
</body>
</html>

iframe.html

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script>
function UploadFile(){
var fileObj = document.getElementById("img").files[0]; var form = new FormData();
form.append("k1", "v1");
form.append("fff", fileObj); var xhr = new XMLHttpRequest();
xhr.open("post", '/index', true);
xhr.send(form);
}
</script>
</body>
</html>

HTML - XMLHttpRequest

<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="" type="file" />
<input id="formsubmit" type="submit" value="Send File" />
</form> <div id="textarea">
</div>

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