zoukankan      html  css  js  c++  java
  • python基础-第十三篇-13.2Web框架之Tornado

    • Tornado是非阻塞异步web frame,而且速度相当快,得力于其非阻塞的方式和对epoll的运用
    • Tornado每秒可以处理数以千计的链接,所以它可以有效的处理C10K问题

    下载安装

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

    框架应用

    一、快速上手

    # 第一步:导模块
    import tornado.ioloop
    import tornado.web
    
    # 第二步:创建类,必须继承tornado.web.RequestHandler,按照自己的业务逻辑重写get方法或post方法
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            self.write("Hello, world")
    
    # 第三步:实例Application对象,构建路由系统
    application = tornado.web.Application([
        (r"/index", MainHandler),
    ])
    
    
    if __name__ == "__main__":
        # 第四步:socket运行起来
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    

    更多路径配置

        'template_path': 'views',        # html文件
        'static_path': 'statics',        # 静态文件(css,js,img)
        'static_url_prefix': '/statics/',# 静态文件前缀
        'cookie_secret': 'suoning',      # cookie自定义字符串加盐
        # 'xsrf_cookies': True,          # 防止跨站伪造
        # 'ui_methods': mt,              # 自定义UIMethod函数
        # 'ui_modules': md,              # 自定义UIModule类
    

    执行过程:

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

    二、路由系统

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

    import tornado.ioloop
    import tornado.web
    
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            self.write("Hello, world")
    
    class StoryHandler(tornado.web.RequestHandler):
        def get(self, story_id):
            self.write("You requested the story " + story_id)   
    
    class BuyHandler(tornado.web.RequestHandler):
        def get(self):
            self.write("buy.wupeiqi.com/index")
    
    application = tornado.web.Application([
        (r"/index", MainHandler),
        (r"/story/([0-9]+)", StoryHandler),   #基于此实现分页功能
    ])
    
    application.add_handlers('buy.wupeiqi.com$', [
        (r'/index',BuyHandler),               #这里添加2级域名,测试的话需要改本地host
    ])
    
    if __name__ == "__main__":
        application.listen(80)
        tornado.ioloop.IOLoop.instance().start()
    

    三、模板引擎

      模板引擎说简单点就是将原来的html的某些内容用一些特殊的字符串代替,然后再处理用户的不同请求时,将html的字符串替换掉,返回给用户新的一个字符串,这样就达到了动态的html的效果。

      Tornado的模板支持“控制语句”和“表达语句”,控制语句格式{% python语句 %} 例如:{% for item in range(10)%},表达语句格式{{变量}} 比如:{{item}},对于控制语句在逻辑结尾的地方还要写上{% end %}

      不仅提供通过UIMethod和UIModule来自定义方法和模块,而且Tornado本身就提供了一些方法,其中<link href="{{static_url("commons.css")}}" rel="stylesheet" /> static_url方法可以实现静态文件缓存(更新的内置方法见骚师博客)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>{{name}}</h1>
        {% for item in user_list %}
        <li>{{item}}</li>
        {% end %}
    </body>
    </html>
    index.html
    import tornado.ioloop
    import tornado.web
    
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            self.render("index.html",name="alex",user_list=[11,22,33])
    
    
    settings = {
        'template_path':'views',
    }
    
    application = tornado.web.Application([
        (r"/index", MainHandler),
    ],**settings)
    
    
    if __name__ == "__main__":
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    

       自定义UIMethod和UIModule:通过模板语言的自定义功能,可以让你使用更加熟悉的python代码来实现动态的模板渲染,其中UIMethod中定义函数,UIModule中定义类

      实现自定义方法三步走:

    • 创建UIMethod.py UIModule.py,定义方法和类(方法定义的时候,必须传入self;类中必须要有render方法,功能代码实现就在这个方法里)
    # uimethods.py
     
    def tab(self):
        return 'UIMethod'
    
    #!/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>wupeiqi</h1>')
            #return escape.xhtml_escape('<h1>wupeiqi</h1>')
    
    • 导入创建文件,在settings里注册
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    import tornado.ioloop
    import tornado.web
    from tornado.escape import linkify
    import uimodules as md
    import uimethods as mt
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            self.render('index.html')
    
    settings = {
        'template_path': 'template',
        'static_path': 'static',
        'static_url_prefix': '/static/',
        'ui_methods': mt,
        'ui_modules': md,
    }
    
    application = tornado.web.Application([
        (r"/index", MainHandler),
    ], **settings)
    
    
    if __name__ == "__main__":
        application.listen(8009)
        tornado.ioloop.IOLoop.instance().start()
    
    • 模块中调用 方法:{{ func() }}  类:{% module 类名() %}
    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <link href="{{static_url("commons.css")}}" rel="stylesheet" />
    </head>
    <body>
        <h1>hello</h1>
        {% module custom(123) %}
        {{ tab() }}
    </body>
    

    四、模板继承和静态缓存

      将一些公用的html,css等写到通用的文件,然后通过继承,就可以获取母版的内容,而继承的html里面只需要写特有的东西,模板继承的功能非常实用,而静态缓存则可以减少相应的请求资源。

    母版

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <link href="{{static_url('css/chouti.css')}}" type="text/css" rel="stylesheet">  // 通过static_url引用静态文件
        {% block css %} {% end %}
    </head>
    <body>
        <div class="header">
            <div class="header-content">     
                {% if user_info['is_login'] %}
                    <div class="account">
                        <a href="#">{{user_info['username']}}</a>
                        <a href="/logout">退出</a>
                    </div>
                {% else %}
                    <div class="account">
                        <a href="http://127.0.0.1:8888/register">注册</a>
                        <a href="http://127.0.0.1:8888/login">登陆</a>
                    </div>
                {% end %}
            </div>
        </div>
        <div class="content">
            {% block body %}
            {% end %}      
        </div>
        <a class="back-to-head" href="javascript:scroll(0,0)"></a>
        {% block js %} {% end %}
        <script>
     
        </script>
    </body>
    </html>
    

     子版

    {% extends '../base/layout.html' %}
    {% block css %}
    <link href="{{static_url('css/css/common.css')}}" rel="stylesheet">
    <link href="{{static_url('css/css/login.css')}}" rel="stylesheet">
    {% end %}
     
     
    {% block body %}
     
    {% end %}
     
    {% block js %}
        <script src="{{static_url('js/jquery-1.12.4.js')}}"></script>
        <script src="{{static_url('js/login.js')}}"></script>
    {% end %}
    

    五、Xss和csrf

    • Xss跨站脚本攻击

      恶意攻击者往web页面里插入恶意script代码,当用户浏览该页时,嵌入web里面的script代码会被执行,从而达到恶意攻击用户的特殊目的

    •  csrf跨站请求伪造(对post请求限制)

      get请求的时候,会给浏览器发一个id,浏览器post请求的时候,携带这个id,然后服务端对其做验证,如果没有这个id的话,就禁止浏览器提交内容

      在Tornado里需要在settings里配置“xsrf_cookies”:True,如果这样做,Tornado将拒绝浏览器请求参数中不包含正确的_xsrf值的post/put/delete请求,并禁止其访问

    settings = {
        "xsrf_cookies": True,
    }
    application = tornado.web.Application([
        (r"/", MainHandler),
        (r"/login", LoginHandler),
    ], **settings)
    配置
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="/sss/jquery-1.12.4.js"></script>
        <!--<script src="{{ static_url('jquery-1.12.4.js') }}" ></script>-->
    </head>
    <body>
        <!--{{ xsrf_form_html() }}-->
        {% raw xsrf_form_html() %}
     
        <input type="button" value="ajax_csrf" onclick="SubmitCsrf();">
     
        <script>
          
            function getCookie(name) {
                var r = document.cookie.match("\b" + name + "=([^;]*)\b");
                return r ? r[1] : undefined;
            }
     
            function SubmitCsrf() {
                var nid = getCookie('_xsrf');
                console.log(nid);
                $.post({
                    url: '/csrf',
                    data:{'k1':'v1', "_xsrf":nid},
                    success:function (callback) {
                        console.log(callback);
                    }
                });
            }
        </script>
    </body>
    </html> 
    View Code

    六、上传文件

    1、Form表单上传

    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>上传文件</title>
    </head>
    <body>
        <form id="my_form" name="form" action="/index" method="POST"  enctype="multipart/form-data" >
            <input name="fff" id="my_file"  type="file" />
            <input type="submit" value="提交"  />
        </form>
    </body>
    </html>
    index.html
    import tornado.ioloop
    import tornado.web
    
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
    
            self.render('index.html')
    
        def post(self, *args, **kwargs):
            #获取文件方法self.request.files
            file_metas = self.request.files["fff"]
            # print(file_metas)
            #[{'filename':'文件名','body':'文件内容']
            for meta in file_metas:
                file_name = meta['filename']
                with open(file_name,'wb') as up:
                    up.write(meta['body'])
    
    settings = {
        'template_path': 'views',
    }
    
    application = tornado.web.Application([
        (r"/index", MainHandler),
    ], **settings)
    
    
    if __name__ == "__main__":
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    

     2、AJAX上传

    HTML - XMLHttpRequest

    <!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];
    
                //创建Formdata对象,作为文件对象的载体
                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 - jQuery

    <!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 = $("#img")[0].files[0];
                var form = new FormData();
                form.append("k1", "v1");
                form.append("fff", fileObj);
    
                $.ajax({
                    type:'POST',
                    url: '/index',
                    data: form,
                    processData: false,  // tell jQuery not to process the data
                    contentType: false,  // tell jQuery not to set contentType
                    success: function(arg){
                        console.log(arg);
                    }
                })
            }
        </script>
    </body>
    </html>
    
    import tornado.ioloop
    import tornado.web
    
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
    
            self.render('index.html')
    
        def post(self, *args, **kwargs):
            #获取文件方法self.request.files
            file_metas = self.request.files["fff"]
            # print(file_metas)
            #[{'filename':'文件名','body':'文件内容']
            for meta in file_metas:
                file_name = meta['filename']
                with open(file_name,'wb') as up:
                    up.write(meta['body'])
    
    settings = {
        'template_path': 'views',
    }
    
    application = tornado.web.Application([
        (r"/index", MainHandler),
    ], **settings)
    
    
    if __name__ == "__main__":
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    py

     

    七、验证码

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

    • 安装图像处理模块:pip3 install pillow
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="statics/jquery-1.12.4.js"></script>
    </head>
    <body>
        <form action="login" method="post">
            <input type="text", name="code">
            <img src="/check_code" onclick="ChangeCode();" id="imgcode">
            <input type="submit" value="submit">
            <span>{{status}}</span>
        </form>
        <script>
            function ChangeCode() {
                var code = document.getElementById('imgcode');
                code.src += '?'
            }
        </script>
    </body>
    </html>
    html
    class CheckCodeHandler(BaseHandler):
        def get(self, *args, **kwargs):
            import io
            import check_code
            mstream = io.BytesIO()
            img, code = check_code.create_validate_code()
            img.save(mstream, 'GIF')
            self.session['CheckCode'] = code
            self.write(mstream.getvalue())
    py

    八、自定义分页类

      分页功能十分常见的,所以整理成一个类,当做一个小组件来使用是非常有必要的

      在前端注意因为xxs不能显示页面的情况,{% raw data %}

    class Pagenation:
        def __init__(self, current_page, all_item, each_item):
     
            #all_pager总页数,c余数
            all_pager, c = divmod(all_item, each_item)
            #余数不为0,总页数要加1
            if c > 0:
                all_pager += 1
            #如果客户在url里没输入页码,默认当前页为1
            if current_page == '':
                current_page = 1
            self.current_page = int(current_page)  # 当前页
            self.all_pages = all_pager  # 总的页面数
            self.each_item = each_item  # 每页显示的item数
     
        @property
        def start_item(self):  # 当前页的起始item位置
            return (self.current_page - 1) * self.each_item
     
        @property
        def end_item(self):  # 当前页结束item位置
            return self.current_page * self.each_item
     
        @property
        def start_end_span(self):  # 获取开始和结束页的具体数字
            if self.all_pages < 10:
                start_page = 1  # 起始页
                end_page = self.all_pages + 1  # 结束页
            else:  # 总页数大于10
                if self.current_page < 5:
                    start_page = 1
                    end_page = 11
                else:
                    if (self.current_page + 5) < self.all_pages:
                        start_page = self.current_page - 4
                        end_page = self.current_page + 5 + 1
                    else:
                        start_page = self.all_pages - 10
                        end_page = self.all_pages + 1
            return start_page, end_page
     
        def generate_str_page(self):
            list_page = []
            start_page, end_page = self.start_end_span
     
            if self.current_page == 1:  # 上一页
                prev = '<li><a class="pre-page" href="javascript:void(0);">上一页</a></li>'
            else:
                prev = '<li><a class="pre-page" href="/index/%s">上一页</a></li>' % (self.current_page - 1,)
            list_page.append(prev)
     
            for p in range(start_page, end_page):  # 1-10
                if p == self.current_page:
                    temp = '<li><a class="li-page" href="/index/%s">%s</a></li>' % (p, p)
                else:
                    temp = '<li><a href="/index/%s">%s</a></li>' % (p, p)
                list_page.append(temp)
     
            if self.current_page == self.all_pages:  # 下一页
                nex = '<li><a class="next-page" href="javascript:void(0);">下一页</a></li>'
            else:
                nex = '<li><a class="next-page" href="/index/%s">下一页</a></li>' % (self.current_page + 1,)
            list_page.append(nex)
     
            # 跳转
            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
    

    更多详细内容请见骚师博客:http://www.cnblogs.com/wupeiqi/articles/5702910.html

    王冬web详解http://www.cnblogs.com/Wxtrkbc/p/5704022.html

    Tornado组件http://www.cnblogs.com/Wxtrkbc/p/5710471.html

  • 相关阅读:
    罗杨美慧 20180912-3 词频统计
    罗杨美慧 20190912-2 命令行
    罗杨美慧 20190905-1 每周例行报告
    罗杨美慧 20190905-2 博客作业
    20190919-4 测试,结对要求
    孙晓宇-20190912-1 每周例行报告
    孙晓宇-20180912-3 词频统计
    (第二周)孙晓宇20190912-2 命令行
    孙晓宇20190905-2 博客作业
    孙晓宇20190905-1 每周例行报告
  • 原文地址:https://www.cnblogs.com/xinsiwei18/p/5816263.html
Copyright © 2011-2022 走看看