web框架 之 Tornado

初识 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,他内部生成的路由映射的时候相当于(二级域名的方式)下图:

我们可以通过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','111')     #为浏览器设置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解密,获取值内容

 

#!/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中

 

#!/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="1">
        <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="1"/>暴虐的;
        <input type="checkbox" name="favor" value="2"/>温和的;
        <input type="checkbox" name="favor" value="3"/>傻二的;
        <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','1');
            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','1');
            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','1');
            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="50" type="file" />
    <input id="formsubmit" type="submit" value="Send File" />
</form>
 
<div id="textarea">
</div>
扩展:基于iframe实现Ajax上传示例

 

posted @ 2016-07-27 23:40  起来了皮  阅读(1303)  评论(0编辑  收藏  举报