• 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=" 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=" 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上传示例
  • 相关阅读:
    JAVA访问权限控制[zhuan]
    Netstat简介
    查看cpu性能和磁盘空间
    简单linux查询
    linux 三剑客命令(grep,sed ,awk)
    同步、异步的使用场景及好处
    AJAX中同步和异步的区别和使用场景
    在SpringBoot中用SpringAOP实现日志记录功能
    springboot使用@Aspect实现AOP记录日志讲解
    Spring:获取容器中的Bean
  • 原文地址:https://www.cnblogs.com/guokaixin/p/5713136.html
Copyright © 2020-2023  润新知