zoukankan      html  css  js  c++  java
  • Python之路【第二十篇】其他WEB框架

    WEB框架功能分析

    WEB框架本质上,就是一个SOCKET Server

    WEB框架前面有WSGI或者是自己写的SOCKET,然后交给URL路由系统处理,然后交给某个函数或某个类,然后在模板里拿到模板然后模板和数据进行混合然后返回给用户!

    WSGI用来接收请求,然后封装请求。对于Django来说都封装到了request里面。

    把整个WEB框架的环整明白了在学习其他的WEB框架就简单多了。

    回顾下Django的生命周期

    Python的WEB框架们

    一、Bottle

    Bottle是一个快速、简洁、轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Python的标准库外,其不依赖任何其他模块。

    1、安装Bottle

    pip install bottle
    easy_install bottle
    apt-get install python-bottle
    wget http://bottlepy.org/bottle.py

    2、Bottle框架大致可以分为以下部分:

    • 路由系统,将不同请求交由指定函数处理
    • 模板系统,将模板中的特殊语法渲染成字符串,值得一说的是Bottle的模板引擎可以任意指定:Bottle内置模板、makojinja2cheetah
    • 公共组件,用于提供处理请求相关的信息,如:表单数据、cookies、请求头等
    • 服务,Bottle默认支持多种基于WSGI的服务,如:
    server_names = {
        'cgi': CGIServer,
        'flup': FlupFCGIServer,
        'wsgiref': WSGIRefServer,
        'waitress': WaitressServer,
        'cherrypy': CherryPyServer,
        'paste': PasteServer,
        'fapws3': FapwsServer,
        'tornado': TornadoServer,
        'gae': AppEngineServer,
        'twisted': TwistedServer,
        'diesel': DieselServer,
        'meinheld': MeinheldServer,
        'gunicorn': GunicornServer,
        'eventlet': EventletServer,
        'gevent': GeventServer,
        'geventSocketIO':GeventSocketIOServer,
        'rocket': RocketServer,
        'bjoern' : BjoernServer,
        'auto': AutoServer,
    }

    3、使用bottle框架

    导入模块后,听过定位可以查看到Bottle模块本身就一个文件,我们把这一个文件导入了就可以使用了,所以他提供的功能是有限的。

    对于bottle他自己本身没有实现SOCKET所以他是借用其他WSGI,并且他本身也没有模板引擎!

    框架如果要运行最起码需要WSGI、模板引擎

    本身Bottle不以来任何Python模块他只以来Python版本库!只有在运行的时候才需要以来其他的Python模块。

    from bottle import template, Bottle
    root = Bottle()
     
    @root.route('/hello/')
    def index():
        return "Hello World"
        # return template('<b>Hello {{name}}</b>!', name="Alex")
     
    root.run(host='localhost', port=8080)

    4、路由系统

    路由系统是的url对应指定函数,当用户请求某个url时,就由指定函数处理当前请求,对于Bottle的路由系统可以分为一下几类:

    • 静态路由
    • 动态路由
    • 请求方法路由
    • 二级路由

    对于bottle的路由来说,指定的路由对应指定的函数

    4.1、静态路由

    @root.route('/hello/')
    def index():
        return template('<b>Hello {{name}}</b>!', name="Shuaige")

    并且可以绑定多个装饰器如代码(当你访问hello的时候回执行下面的函数,当你访问index的时候也会执行下面的index函数)

    @root.route('/index/')
    @root.route('/hello/')
    def index():
        #return "Hello World"
        return template('<b style="background-color:red">Hello {{name}}</b>!', name="Tim")
    
    root.run(host='localhost', port=8080)

    4.2、动态路由(支持正则)

    举例来说:

    @root.route('/wiki/<pagename>')
    def callback(pagename):
        ...
    
    当请求过来之后@root.route('/wiki/<pagename>') 这个pagename值就会自动把值赋值给def callback(pagename):的参数了!
    @root.route('/wiki/<pagename>')
    def callback(pagename):
        ...
     
    @root.route('/object/<id:int>')
    def callback(id):
        ...
     
    @root.route('/show/<name:re:[a-z]+>')
    def callback(name):
        ...
     
    @root.route('/static/<path:path>')
    def callback(path):
        return static_file(path, root='static')

    单独说下:@root.route('/static/<path:path>'),正常情况下URL的设置如:/hello/那么这个URL就结束了,如果

    4.3、请求方法路由

    在http访问请求中有很多请求方法比如get、post、delete等

    如果使用了@root.get就表示这个装饰器下的函数只接收get请求!

    @root.route('/hello/', method='POST')
    def index():
        ...
     
    @root.get('/hello/')
    def index():
        ...
     
    @root.post('/hello/')
    def index():
        ...
     
    @root.put('/hello/')
    def index():
        ...
     
    @root.delete('/hello/')
    def index():
        ...

    4.4、二级路由

    就和Django中的多个APP把不同app下的请求转发到不同的app下面进行处理一样!

    index

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from bottle import template, Bottle
    from bottle import static_file
    root = Bottle()
     
    @root.route('/hello/')
    def index():
        return template('<b>Root {{name}}</b>!', name="Alex")
     
    from framwork_bottle import app01
    from framwork_bottle import app02
     
    root.mount('app01', app01.app01)
    root.mount('app02', app02.app02)
     
    root.run(host='localhost', port=8080)

    app01&app02

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from bottle import template, Bottle
    
    app01 = Bottle()
    
    @app01.route('/hello/', method='GET')
    def index():
        return template('<b>App01</b>!')
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from bottle import template, Bottle
    
    app02 = Bottle()
    
    
    @app02.route('/hello/', method='GET')
    def index():
        return template('<b>App02</b>!')

    5、模板系统

    Bottle本身有自己的模板渲染语言的,但是他也可以使用:makojinja2cheetah等!

    bottle自己的模板语言例子:

    html

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>{{name}}</h1>
    </body>
    </html>

    index.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from bottle import template, Bottle
    root = Bottle()
     
    @root.route('/hello/')
    def index():
        # 默认情况下去目录:['./', './views/']中寻找模板文件 hello_template.html
        # 配置在 bottle.TEMPLATE_PATH 中
        return template('hello_template.tpl', name='shuaige')
     
    root.run(host='localhost', port=8080)

    5.2、语法

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>1、单值</h1>
          {{name}}
        #应用后端返回的数据
    
        <h1>2、单行Python代码</h1>
            % s1 = "hello"
            {{ s1 }}
        #'%s1 这里设置一个变量,我们可以在html调用和python类似'
    
        <h1>3、Python代码块</h1>
        <%
            # python块代码
            name = name.title().strip()
            if name == "shuaige":
                name="luotianshuai"
        %>
    
        <h1>4、Python、Html混合</h1>
    
        % if True:
            <span>{{name}}</span>
        % end
        <ul>
          % for item in name:
            <li>{{item}}</li>
          % end
        </ul>
    
    </body>
    </html>

    5.3、函数

    include(sub_template, **variables)

    # 导入其他模板文件
     
    % include('header.tpl', title='Page Title')
    Page Content
    % include('footer.tpl')

    rebase(name, **variables)

    <html>
    <head>
      <title>{{title or 'No title'}}</title>
    </head>
    <body>
      {{!base}}
    </body>
    </html>
    # 导入母版
     
    % rebase('base.tpl', title='Page Title')
    <p>Page Content ...</p>
    defined(name) #检查当前变量是否已经被定义,已定义True,未定义False
    get(name, default=None) #获取某个变量的值,不存在时可设置默认值
    setdefault(name, default) #如果变量不存在时,为变量设置默认值

    5.4、扩展自定义函数

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>自定义函数</h1>
        {{ shuaige() }}
    
    </body>
    </html>

    index.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from bottle import template, Bottle,SimpleTemplate
    root = Bottle()
    
    
    def custom():
        return '123123'
    
    
    @root.route('/hello/')
    def index():
        # 默认情况下去目录:['./', './views/']中寻找模板文件 hello_template.html
        # 配置在 bottle.TEMPLATE_PATH 中
        return template('hello_template.html', name='tianshuai', shuaige=custom)
    
    root.run(host='localhost', port=8080)

    6、公共组件

    由于Web框架就是用来【接收用户请求】-> 【处理用户请求】-> 【响应相关内容】,对于具体如何处理用户请求,开发人员根据用户请求来进行处理,而对于接收用户请求和相应相关的内容均交给框架本身来处理,其处理完成之后将产出交给开发人员和用户。

    【接收用户请求】

    当框架接收到用户请求之后,将请求信息封装在Bottle的request中,以供开发人员使用

    【响应相关内容】

    当开发人员的代码处理完用户请求之后,会将其执行内容相应给用户,相应的内容会封装在Bottle的response中,然后再由框架将内容返回给用户

    所以,公共组件本质其实就是为开发人员提供接口,使其能够获取用户信息并配置响应内容。

    6.1、request

    Bottle中的request其实是一个LocalReqeust对象,其中封装了用户请求的相关信息:

    request.headers
    #请求头信息,可以通过请求头信息来获取相关客户端的信息
     
    request.query
    #get请求信息,如果用户访问时这样的:http://127.0.0.1:8000/?page=123就必须使用request.query  使用GET方法是无法取到信息的
     
    request.forms
    #post请求信息
     
    request.files
    #上传文件信息
     
    request.params
    #get和post请求信息,他是GET和POST的总和,其实他内部调用了request.get request.forms
     
    request.GET
    #get请求信息
     
    request.POST
    #post和上传信息,上传文件信息,和post信息
     
    request.cookies
    #cookie信息
         
    request.environ
    #环境相关相关,如果上面的这些请求信息没有满足你的需求,就在这里找!

    Flask框架

    Flask相对于bottle来说,他要优于bottle相对于bottle来说,Flask有很多插件供其使用!

    Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。

    “微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。

    默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。

    flask它自己没有Socket也没有WSGI,并且也没有模板引擎!

    1、安装

    pip install Flask

    2、werkzeug

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from werkzeug.wrappers import Request, Response
    
    @Request.application
    def hello(request):
        return Response('Hello World!')
    
    if __name__ == '__main__':
        from werkzeug.serving import run_simple
        run_simple('localhost', 4000, hello)

    上面和bottle一样通过werkzeug封装之后然后返回给Flask框架

    3、第一个Flask程序(仔细看几大块和bottle类似)

    from flask import Flask
    app = Flask(__name__)
     
    @app.route("/")
    def hello():
        return "Hello World!"
     
    if __name__ == "__main__":
        app.run()

    上面的代码是什么意思呢?

    那么,这些代码是什么意思呢?
    
    1、首先我们导入了 Flask 类。这个类的实例将会成为我们的 WSGI 应用。
    2、接着我们创建了这个类的实例。第一个参数是应用模块或者包的名称。如果你使用一个 单一模块(就像本例),那么应当使用 __name__ ,因为名称会根据这个模块是按 应用方式使用还是作为一个模块导入而发生变化(可能是 '__main__' ,也可能是 实际导入的名称)。这个参数是必需的,这样 Flask 就可以知道在哪里找到模板和 静态文件等东西。更多内容详见 Flask 文档。
    3、然后我们使用 route() 装饰器来告诉 Flask 触发函数的 URL 。
    4、函数名称可用于生成相关联的 URL ,并返回需要在用户浏览器中显示的信息。
    5、最后,使用 run() 函数来运行本地服务器和我们的应用。 if __name__ == '__main__': 确保服务器只会在使用 Python 解释器运行代码的 情况下运行,而不会在作为模块导入时运行。

    4、路由系统

    • @app.route('/user/<username>')
    • @app.route('/post/<int:post_id>')
    • @app.route('/post/<float:post_id>')
    • @app.route('/post/<path:path>')
    • @app.route('/login', methods=['GET', 'POST'])

    首先看这里的flask和bottle的区别,首先bottle这里的参数只能是一个方法,但是flask和传多个方法!并且flask默认是不支持正则表达式的!(注:对于Flask默认不支持直接写正则表达式的路由,不过可以通过自定义来实现,见:https://segmentfault.com/q/1010000000125259)

    常用路由系统有以下五种,所有的路由系统都是基于一下对应关系来处理:

    DEFAULT_CONVERTERS = {
        'default':          UnicodeConverter,
        'string':           UnicodeConverter,
        'any':              AnyConverter,
        'path':             PathConverter,
        'int':              IntegerConverter,
        'float':            FloatConverter,
        'uuid':             UUIDConverter,
    }

    5、模板

    5.1、模板使用

    Flask使用的是Jinja2模板,所以其语法和Django无差别

    5.2、自定义模板语言

    Flask中自定义模板方法的方式和Bottle相似,创建一个函数并通过参数的形式传入render_template,如:

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>自定义函数</h1>
        {{ww()|safe}}
    
    </body>
    </html>
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Flask,render_template
    app = Flask(__name__)
     
     
    def shuaige():
        return '<h1>shuaige</h1>'
     
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        return render_template('login.html', ww=shuaige)
     
    app.run()
    Flask 会在 templates 文件夹内寻找模板。因此,如果你的应用是一个模块,那么模板 文件夹应该在模块旁边;如果是一个包,那么就应该在包里面:
    
    情形 1: 一个模块:
    
    /application.py
    /templates
        /hello.html
    情形 2: 一个包:
    
    /application
        /__init__.py
        /templates
            /hello.html
    你可以充分使用 Jinja2 模板引擎的威力。

    6、公共组件

    对于Http请求,Flask会讲请求信息封装在request中(werkzeug.wrappers.BaseRequest),提供的如下常用方法和字段以供使用:

    request.method
    request.args
    request.form
    request.values
    request.files
    request.cookies
    request.headers
    request.path
    request.full_path
    request.script_root
    request.url
    request.base_url
    request.url_root
    request.host_url
    request.host

    表单处理

    @app.route('/login', methods=['POST', 'GET'])
    def login():
        error = None
        if request.method == 'POST':
            if valid_login(request.form['username'],
                           request.form['password']):
                return log_the_user_in(request.form['username'])
            else:
                error = 'Invalid username/password'
        # the code below is executed if the request method
        # was GET or the credentials were invalid
        return render_template('login.html', error=error)

    上传文件

    from flask import request
    from werkzeug import secure_filename
    
    @app.route('/upload', methods=['GET', 'POST'])
    def upload_file():
        if request.method == 'POST':
            f = request.files['the_file']
            f.save('/var/www/uploads/' + secure_filename(f.filename))
        ...

    cookie操作

    from flask import request
    
    @app.route('/setcookie/')
    def index():
        username = request.cookies.get('username')
        # use cookies.get(key) instead of cookies[key] to not get a
        # KeyError if the cookie is missing.
    
    
    
    
    from flask import make_response
    
    @app.route('/getcookie')
    def index():
        resp = make_response(render_template(...))
        resp.set_cookie('username', 'the username')
        return resp

    6.2、响应

    当用户请求被开发人员的逻辑处理完成之后,会将结果发送给用户浏览器,那么就需要对请求做出相应的响应。

    字符串

    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        return "index"

    模板引擎

    from flask import Flask,render_template,request
    app = Flask(__name__)
     
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        return render_template("index.html")
     
    app.run()

    重定向

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Flask, redirect, url_for
    app = Flask(__name__)
     
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        # return redirect('/login/')
        return redirect(url_for('login'))
     
    @app.route('/login/', methods=['GET', 'POST'])
    def login():
        return "LOGIN"
     
    app.run()

    错误页面

      --指定URL,简单错误

    from flask import Flask, abort, render_template
    app = Flask(__name__)
    
    @app.route('/e1/', methods=['GET', 'POST'])
    def index():
        abort(404, 'Nothing')
    app.run()
    from flask import Flask, abort, render_template
    app = Flask(__name__)
     
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        return "OK"
     
    @app.errorhandler(404)
    def page_not_found(error):
        return render_template('page_not_found.html'), 404
     
    app.run()

    设置相应信息

    使用make_response可以对相应的内容进行操作

    from flask import Flask, abort, render_template,make_response
    app = Flask(__name__)
     
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        response = make_response(render_template('index.html'))
        # response是flask.wrappers.Response类型
        # response.delete_cookie
        # response.set_cookie
        # response.headers['X-Something'] = 'A value'
        return response
     
    app.run()

    6.3、Session

    除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。

    • 设置:session['username'] = 'xxx'

    • 删除:session.pop('username', None)
    from flask import Flask, session, redirect, url_for, escape, request
     
    app = Flask(__name__)
     
    @app.route('/')
    def index():
        if 'username' in session:
            return 'Logged in as %s' % escape(session['username'])
        return 'You are not logged in'
     
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            session['username'] = request.form['username']
            return redirect(url_for('index'))
        return '''
            <form action="" method="post">
                <p><input type=text name=username>
                <p><input type=submit value=Login>
            </form>
        '''
     
    @app.route('/logout')
    def logout():
        # remove the username from the session if it's there
        session.pop('username', None)
        return redirect(url_for('index'))
     
    # set the secret key.  keep this really secret:
    app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

    更多参考:http://www.cnblogs.com/wupeiqi/articles/5341480.html

    非常不错的Flask网站:https://dormousehole.readthedocs.org/en/latest/

  • 相关阅读:
    1014 Waiting in Line (30)(30 point(s))
    1013 Battle Over Cities (25)(25 point(s))
    1012 The Best Rank (25)(25 point(s))
    1011 World Cup Betting (20)(20 point(s))
    1010 Radix (25)(25 point(s))
    1009 Product of Polynomials (25)(25 point(s))
    1008 Elevator (20)(20 point(s))
    1007 Maximum Subsequence Sum (25)(25 point(s))
    1006 Sign In and Sign Out (25)(25 point(s))
    1005 Spell It Right (20)(20 point(s))
  • 原文地址:https://www.cnblogs.com/luotianshuai/p/5386494.html
Copyright © 2011-2022 走看看