A request to flask

2015-12-09

有感于之前看到的 repo: what-happens-when

What happens when you type google.com into your browser and press enter?

于是决定今天来说说当一个普通的 Get 请求来的时候, Flask 中到底发生了什么事情。 我以最简单的 Hello word 的例子来说明, 当我们访问 http://localhost:5000/ 时, Flask 的内部处理是怎样的。

from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
    return 'Hello, world.'


if __name__ == '__main__':
    app.run(debug=True)

当然,只是个人理解,错误指出还请指出来 _(:3

简单概括

  • preprocess request
  • url map, dispatch request
  • postprocess response

具体展开

简单来说就是依据 request 的 URL 找到对应绑定的 view function [1],执行然后返回 response 就行。 最基本的简单思路大致是这样。在 Opening The Flask 中,简化版本的 flask 例子中的思路就是这样。

from werkzeug import Request, Response, run_simple
from werkzeug.exceptions import HTTPException
from werkzeug.routing import Map, Rule

class YourFlask(object):
    def __init__(self):
        self.url_map = Map()
        self.views = {}

    def route(self, string, **options):
        def decorator(f):
            options['endpoint'] = f.__name__
            self.views[f.__name__] = f
            self.url_map.add(Rule(string, **options))
            return f
        return decorator

    def run(self, **options):
        return run_simple('localhost', 5000, self, **options)

    def make_response(self, rv):
        if isinstance(rv, basestring):
            return Response(rv, mimetype='text/html')
        return rv

    def __call__(self, environ, start_response):
        request = Request(environ)
        adapter = self.url_map.bind_to_environ(environ)
        try:
            endpoint, values = adapter.match()
            response = self.make_response(self.views[endpoint](request, **values))
        except HTTPException, e:
            response = e
        return response(environ, start_response)

以上的是使用 werkzeug 这个库写的一个简单的 web framework,但是基本把 flask 的思路表现出来了。

现实中的 flask 也是采用类似的思路,加上请求预处理和响应后处理。接下来以 flask 0.1 版本来说明一下:

def wsgi_app(self, environ, start_response):
    with self.request_context(environ):
        rv = self.preprocess_request()
        if rv is None:
            rv = self.dispatch_request()
        response = self.make_response(rv)
        response = self.process_response(response)
        return response(environ, start_response)

可以看到,首先是请求的预处理 self.preprocess_request()。这会把我们使用 before_request 注册的函数依次调用一遍, 比如说数据库的连接或者相关数据的预处理等等。值得注意的是,如果在预处理时有有效的结果,这时候是会跳过将请求分发给 view 函数这一步, 也就是说 URL 对应的 view 函数将不会执行。然后是调用 make_response 将返回的结果转换成一个有效的 response object。 接下来是响应后处理。同预处理一样,会将我们使用 after_request 注册的函数依次调用,比如说断开数据库的 连接等操作。

在目前的 flask 0.10.1 版本中,主要代码逻辑是这样的:

def wsgi_app(self, environ, start_response):
    ctx = self.request_context(environ)
    ctx.push()
    error = None
    try:
        try:
            response = self.full_dispatch_request()
        except Exception as e:
            error = e
            response = self.make_response(self.handle_exception(e))
        return response(environ, start_response)
    finally:
        if self.should_ignore_error(error):
            error = None
        ctx.auto_pop(error)

其相对与 0.1 的变化有:

  • 更为完善的错误异常处理
  • blueprint 的相关处理(new in 0.7) [2]
  • signal 处理相关(new in 0.6) [3]
  • 请求预处理中,先会调用 url_value_processor 注册的函数(new in 0.7) [4]
  • 最后会把 request context pop 掉,在 pop 时,调用 do_teardown_request (使用 tear_down_request 注册的函数), 有必要是也会把 app context pop 掉(会调用 do_teardown_appcontext)

总结

总结一下,一个简单的请求在 flask 中的过程:

  • preprocess request
    • url value process
    • before request hooks
  • url map, dispatch request
  • postprocess response
    • after request hooks
  • pop request context/app context

参考


[1]关于 flask 路由的问题不在这里展开,我的 wiki 有总结 http://wiki.lord63.com/python/flask/flask_route.html
[2]preprocess_request 中可以看到相关代码。
[3]full_dispatch_request 中可以看到相关代码。
[4]preprocess_request 中可以看到相关代码。

Til next time,

lord63 at 16:12