django/tonado/flask/bottle

Django,Flask,Tornado(facebook),Bottle,Butterfly 简单比较

  • 一:常用请求参数的接收

    • Django

    • Tornado

    • Flask

    • Butterfly

      • get 请求

      • post 请求

    • Bottle

      • 高级用法

  • 二:常用请求方式的区分

    • Django

    • Tornado

    • Flask

    • Butterfly

  • 三:静态文件和网页模板的处理

    • Django

    • Tornado

    • Flask

    • bottle

    • Butterfly

  • 四:模板语法操作

    • Django

    • Tornado

    • Flask

    • Butterfly

  • 五:Handler 处理函数的返回值

    • Django

    • Butterfly

  • 六:WSGI 处理

    • Django

    • Bottle

  • 七 启动入口

    • Bottle

  • 八 访问日志

    • bottle

一:常用请求参数的接收

Django

request.GET/POST

Tornado

self.get_query_argument()/get_query_arguments()
self.get_body_argument()/get_body_arguments()
self.get_argument()/get_arguments()

Flask

request.args.get('key')
request.form.get(;key')

Butterfly

放在函数的参数列表中 如下面示例中 str_info, 即为请求参数:

get 请求

get 请求的参数和 handler 函数的参数保持一致 (req 后面的参数)

如请求 http://ip:port/hello?str_info=happy 会将 happy 传到 hello 函数中

def hello(req, str_info):
    isinstance(req, Request)
    return retstat.OK, {"str_info": str_info}, [(__info__, __version__)]

post 请求

httpgateway.read_wsgi_post(req.wsgienv)

Bottle

def route(self, path=None, method='GET', callback=None, name=None, apply=None, skip=None, **config)
    if callable(path):
        path, callback = None, path

    plugins = makelist(apply)
    skiplist = makelist(skip)

    def decorator(callback):
        for rule in makelist(path) or yieldroutes(callback):
            for verb in makelist(method):
                verb = verb.upper()
                cfg = dict(rule=rule, method=verb, callback=callback,
                           name=name, app=self, config=config,
                           apply=plugins, skip=skiplist)
                self.routes.append(cfg)
                cfg['id'] = self.routes.index(cfg)
                self.router.add(rule, verb, cfg['id'], name=name)
                if DEBUG:
                    self.ccache[cfg['id']] = self._build_callback(cfg)
        return callback

    return decorator(callback) if callback else decorator

静态路由, 默认是 GET 请求

#/usr/bin/env python
# coding=utf-8
from bottle import route, run

# 定义路由,即浏览器访问的url
@route('/start) 
def start():
   return " <h1>hello, this is my first bottleprocess</h1>"

# 开启服务,端口是 8000,授受任何 IP 地址访问
run(host='0.0.0.0', port=8000)     

动态路由 -- 动态路由就是可以用 url 传递不同的内容或参数到网页上

#!/usr/bin/env python
# coding=utf-8
from bottle import route,run

@route('/start/<sth>')
def start(sth):
   return "<h1>hello, this is my %s bottleprocess</h1>" % sth

run(host='0.0.0.0',port=8000)

高级用法

注册为类似 butterfly 的方法例子

二:常用请求方式的区分

Django

通过 request.method == "GET" / "POST"进行视图函数中不同请求方式的处理区分
同样可以通过装饰器注解方式:@require_POST @require_GET 方式指定视图函数只能接收那种请求方式

Tornado

可以通过直接重写父类 RequestHandler 中的 get/post/.. 的请求处理方法来实现不同的请求方式的区分

Flask

通过路由装饰器注解的 methods 属性来指定视图处理函数可以接收那种请求方式
@app.route("/", methods=["get", "post"..])

Butterfly

可以通过 req.method == "GET" / "POST" 来区分是 Get 请求还是 POST 请求

备注

Butterfly 在注册路由的时候,如果 is_parse_post 为 True , 则会解析 post 包是否为 json ,并将对应的数据传给后端 handler 中

三:静态文件和网页模板的处理

Django

子模块应用:templates/ 默认保存网页模板;static/ 默认保存静态资源;不需要配置
根项目:settings.py 中通过 TEMPLATES>DIRS 配置网页模板文件夹,通过 STATICFILES_DIRS 配置静态资源文件夹

Django response

                         +------------------------------+
                         |HttpResponseBase(six.Iterator)|
                         +------------------------------+
                                       |
                  |--------------------+------------------------|     
+-----------------+---------------------+     +-----------------+------------+
|StreamingHttpResponse(HttpResponseBase)|     |HttpResponse(HttpResponseBase)|
+---------------------------------------+     +------------------------------+
                  |                               ||||||||   | 
                  V                               ||||||||   V
+---------------------------------------+         |||||||| +---------------------------------------------------------+
|FileResponse(StreamingHttpResponse)    |         |||||||| |HttpResponseRedirectBase(HttpResponse)                   |
+---------------------------------------+         |||||||| +---------------------------------------------------------+
                                                  ||||||||  ||
                                                  ||||||||  |V
                                                  ||||||||  |+------------------------------------------------------+
                                                  ||||||||  ||HttpResponseRedirect(HttpResponseRedirectBase)-----302|
                                                  ||||||||  |+------------------------------------------------------+
                                                  V|||||||  V                                               
    +---------------------------------------------+|||||||  +-------------------------------------------------------+
    |HttpResponseNotModified(HttpResponse)     304||||||||  |HttpResponsePermanentRedirect(HttpResponseRedirectBase)|
    +---------------------------------------------+|||||||  +-------------------------------------------------------+
                                                   V||||||
    +----------------------------------------------+||||||
    |HttpResponseBadRequest(HttpResponse)      400 |||||||
    +----------------------------------------------+||||||
                                                    V|||||
    +-----------------------------------------------+|||||
    |HttpResponseNotFound(HttpResponse)        404  ||||||
    +-----------------------------------------------+|||||
                                                     V||||
    +------------------------------------------------+||||
    |HttpResponseForbidden(HttpResponse)       403   |||||
    +------------------------------------------------+||||
                                                      V|||
    +-------------------------------------------------+|||
    |HttpResponseNotAllowed(HttpResponse)      405    ||||
    +-------------------------------------------------+|||
                                                       V||
    +--------------------------------------------------+||
    |HttpResponseGone(HttpResponse)            410     |||
    +--------------------------------------------------+||
                                                        V|
    +---------------------------------------------------+|
    |HttpResponseServerError(HttpResponse)     500      ||
    +---------------------------------------------------+|
                                                         V
    +----------------------------------------------------+
    |JsonResponse(HttpResponse)                          |
    +----------------------------------------------------+

Tornado

通过 tornado.web.Application 中的配置选项
template_path 配置网页模板文件夹位置,static_path 配置静态资源文件夹位置

Flask

默认 templates/ 中保存网页模板;static/ 中保存静态资源,不需要配置

模板渲染

渲染前的信号 before_render_template.send(app, template=template, context=context)
rv = template.render(context) # 模板渲染
渲染后的信号 template_rendered.send(app, template=template, context=context)

bottle

from bottle import route
from bottle import run
from bottle import static_file

@route('/download/<filename:path>')
def download(filename):
   return static_file(filename,root='/Users/wangbin34/test/',download=filename)

run(host='0.0.0.0',port=8080,debug=True)

Butterfly

默认 templates/ 中保存网页模板;static/ 中保存静态资源,不需要配置

四:模板语法操作

  • 解释模型:解析产生一个数据结构表示模板的结构。渲染阶段遍历那个数据结构,基于找到的指令装配结果文本。

  • 编译模型:解析产生某种形式的可直接执行的代码(比如将模板解析成一个 python 函数)。渲染阶段执行那个代码,产生结果。

Django

默认使用自己的模板语法:Django Template Language:DTL 语法
模板引擎使用解释模型

Tornado

默认使用 jinja 模板语法:~经过一定改造的模板语法
模板引擎使用编译模型

Flask

默认使用第三方的 jinja2 模板语法,是在 DTL 语法的基础上完善的一种专门给 python 使用的模板语法
模板引擎使用编译模型

Butterfly

模板引擎使用编译模型
当然,Butterfly 推荐直接使用 Angularjs 框架

五:Handler 处理函数的返回值

Handler 的返回值就是程序的状态码 / 响应包 body/ 响应包 Header 三个部分

Django

from django.http import JsonResponse, HttpResponse

-----django.http package
from django.http.response import (
    BadHeaderError, FileResponse, Http404, HttpResponse,
    HttpResponseBadRequest, HttpResponseForbidden, HttpResponseGone,
    HttpResponseNotAllowed, HttpResponseNotFound, HttpResponseNotModified,
    HttpResponsePermanentRedirect, HttpResponseRedirect,
    HttpResponseServerError, JsonResponse, StreamingHttpResponse,
)

Butterfly

return retstat.OK, {"randstr": randstr}, [(__info__, __version__)]

> httpstatus: 必须有
> content: 非必须(当返回值为 2 个的时候,第 2 个返回值为 Content)
> headers: 非必须(当返回值为 3 个的时候,第 3 个返回值为 headers)

六:WSGI 处理

Django

位置

django/core/handlers/wsgi.py

代码

class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super(WSGIHandler, self).__init__(*args, **kwargs)
        self.load_middleware()

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)

        response._handler_class = self.__class__

        status = '%d %s' % (response.status_code, response.reason_phrase)
        response_headers = [(str(k), str(v)) for k, v in response.items()]
        for c in response.cookies.values():
            response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
        start_response(force_str(status), response_headers)
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
            response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response

Bottle

def wsgi(self, environ, start_response):
    """ The bottle WSGI-interface. """
    try:
        out = self._cast(self._handle(environ))
        # rfc2616 section 4.3
        if response._status_code in (100, 101, 204, 304) or environ['REQUEST_METHOD'] == 'HEAD':
            if hasattr(out, 'close'): out.close()
            out = []
        start_response(response._status_line, response.headerlist)
        return out
    except (KeyboardInterrupt, SystemExit, MemoryError):
        raise
    except Exception as E:
        if not self.catchall: raise
        err = '<h1>Critical error while processing request: %s</h1> ' % html_escape(environ.get('PATH_INFO', '/'))
        if DEBUG:
            err += '<h2>Error:</h2>\n<pre>\n%s\n</pre>\n' \
                   '<h2>Traceback:</h2>\n<pre>\n%s\n</pre>\n' \
                   % (html_escape(repr(E)), html_escape(format_exc()))
        environ['wsgi.errors'].write(err)
        environ['wsgi.errors'].flush()
        headers = [('Content-Type', 'text/html; charset=UTF-8')]
        start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info())
        return [tob(err)]
处理 handle
    def _handle(self, environ):
        ...
        try:
            self.trigger_hook('before_request') --------------------before_request
            route, args = self.router.match(environ)
            ...
            out = route.call(**args)
            break
        except HTTPResponse as E:
            out = E
            break
        except RouteReset:
            depr(0, 13, "RouteReset exception deprecated",
                        "Call route.call() after route.reset() and "
                        "return the result.")
            route.reset()
            continue
        finally:
            self.trigger_hook('after_request')------------------after_request

七 启动入口

Bottle

def run(app=None,
        server='wsgiref',
        host='127.0.0.1',
        port=8080,
        interval=1,
        reloader=False,
        quiet=False,
        plugins=None,
        debug=None,
        config=None,
        **kargs):
""" Start a server instance. This method blocks until the server terminates.

    :param app: WSGI application or target string supported by :func:`load_app`. (default: :func:`default_app`)
    :param server: Server adapter to use. See :data:`server_names` keys for valid names or pass a :class:`ServerAdapter` subclass. (default: `wsgiref`)
    :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on all interfaces including the external one. (default: 127.0.0.1)
    :param port: Server port to bind to. Values below 1024 require root privileges. (default: 8080)
    :param reloader: Start auto-reloading server? (default: False)
    :param interval: Auto-reloader interval in seconds (default: 1)
    :param quiet: Suppress output to stdout and stderr? (default: False)
    :param options: Options passed to the server adapter.
"""

Bottle 的 Server Adapters 简直可以说是 WSGI Server 的博览会,从这里您可以了解目前比较流行的 WSGI 实现

flup
wsgiref
cherrypy
paste
fapws3
tornado
Google Appengine
twisted
diesel
meinheld
gunicorn
eventlet
gevent
rocket

例子

from bottle import route, run

@route('/hello')
def hello():
    return "Hello World!"

run(host='localhost', port=8080, debug=True)

八 访问日志

bottle

import datetime
@bottle.hook('after_request')
def log_after_request():
    msg = '{ip} - - [{time}] "{method} {uri} {protocol}" {status}'.format(
        ip=bottle.request.environ.get('REMOTE_ADDR'),
        time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        method=bottle.request.environ.get('REQUEST_METHOD'),
        uri=bottle.request.environ.get('PATH_INFO'),
        protocol=bottle.request.environ.get('SERVER_PROTOCOL'),
        status=bottle.response.status_code,
    )
    logger.info(msg)

Last updated