0%

Flask源码之一个请求的旅程(二)

入口

由上一篇文章我们知道 Flask 要有如下函数或者一个实现了 __call__方法的类,才可以被wsgi server调用

1
2
3
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, web!</h1>']
1
2
3
4
5
6
7
class Applications(object):
def __call__(self,environ, start_response)
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, web!</h1>']
# 实现了__call__方法的类就可以像函数一样被调用
# 例如 application=Applications()
# application(environt,start_response)

Flask是怎么做的呢? 我们来看一下简单的 Flask demo

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask

flask_app = Flask(__name__)


@flask_app.route('/')
def hello_world():
return "hello flask"


# python3 my_wsgi_server.py flask_app:flask_app
if __name__ == '__main__':
flask_app.run()

这段代码很简单,先是创建了一个Flask实例(对象),然后调用Flask对象的route方法。

启动项目的时候调用Flask对象的run()方法

我们先不去看run方法内部是如何实现的,因为Flask内置了一个wsgi server,从 run方法进入会涉及到很多 wsgi server 的代码,而生产环境我们一般不用这个 wsgi server,而是用gunicorn或者uwsgi

pycharm中按 ctrl,鼠标左键点击 Flask查看源码

我们可以看到Flask是一个类,那么他一定实现 __call__方法

Flask源码

点击 pycharm左侧的 structure我们可以看到果然有 __call__方法,而且这个 __call__方法的参数是 environ,start_responsewsgi server最后调用的也是这个方法

image-20201224152301791

我们进入到这个方法,随便打个断点

启动项目

接下来我们以 debug的方式启动项目

image-20201224152851475

浏览器访问 http://127.0.0.1:5000/,我们看到 pycharm在 我们的断点处停了下来,__call__方法又调用了wsgi_app方法,参数都一摸一样

image-20201224153123774

再看 wsgi_app方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def wsgi_app(self, environ, start_response):

ctx = self.request_context(environ)
error = None
try:

try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise

return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)

这个方法其实就做了以下几件事

  1. 路由:就是根据你的请求,找到对应的视图函数。例如我们的请求 http://127.0.0.1:5000/ 会被 @flask_app.route('/')匹配到,那么就会调用 hello_world方法
  2. 处理请求上下文和应用上下文
  3. 错误处理
  4. 回调 wsgi server提供的 start_response 方法把结果返回给 wsgi server

接下来分别介绍这几个部分