WSGI
所有的 python web 框架都要遵循 WSGI 协议,如果对 WSGI 不清楚,可以查看我之前的介绍文章。
在这里还是要简单回顾一下 WSGI 的核心概念。
WSGI 中有一个非常重要的概念:每个 python web 应用都是一个可调用(callable)的对象。在 flask 中,这个对象就是 app = Flask(__name__)
创建出来的 app,就是下图中的绿色 Application 部分。要运行 web 应用,必须有 web server,比如我们熟悉的 apache、nginx ,或者 python 中的 gunicorn ,我们下面要讲到的 werkzeug
提供的 WSGIServer
,它们是下图的黄色 Server 部分。
Server 和 Application 之间怎么通信,就是 WSGI 的功能。它规定了 app(environ, start_response)
的接口,server 会调用 application,并传给它两个参数:environ
包含了请求的所有信息,start_response
是 application 处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息。
WSGI application 非常重要的特点是:它是可以嵌套的。换句话说,我可以写个 application,它做的事情就是调用另外一个 application,然后再返回(类似一个 proxy)。一般来说,嵌套的最后一层是业务应用,中间就是 middleware。这样的好处是,可以解耦业务逻辑和其他功能,比如限流、认证、序列化等都实现成不同的中间层,不同的中间层和业务逻辑是不相关的,可以独立维护;而且用户也可以动态地组合不同的中间层来满足不同的需求。
WSGI 的内容就讲这么多,我们来看看 flask 的 hello world 应用:
|
|
这里的 app = Flask(__name__)
就是上面提到的 Application 部分,但是我们并没有看到 Server 的部分,那么它一定是隐藏到 app.run()
内部某个地方了。
启动流程
应用启动的代码是 app.run()
,这个方法的代码如下:
|
|
NOTE:为了阅读方便,我删除了注释和不相干的部分,下面所有的代码都会做类似的处理,不再赘述。
这个方法的内容非常简单:处理一下参数,然后调用 werkzeug
的 run_simple
。需要注意的是:run_simple
的第三个参数是 self
,也就是我们创建的 Flask()
application。因为 WSGI server 不是文章的重点,所以我们就不深入讲解了。现在只需要知道它的功能就行:监听在指定的端口,收到 HTTP 请求的时候解析为 WSGI 格式,然后调用 app 去执行处理的逻辑。对应的执行逻辑在 werkzeug.serving:WSGIRequestHandler
的 run_wsgi
中有这么一段代码:
|
|
可以看到 application_iter = app(environ, start_response)
就是调用代码获取结果的地方。
要调用 app 实例,那么它就需要定义了 __call__
方法,我们找到 flask.app:Flask
对应的内容:
|
|
上面这段代码只有一个目的:找到处理函数,然后调用它。除了异常处理之外,我们还看到了 context
相关的内容(开始有 ctx.push()
,最后有 ctx.auto_pop()
的逻辑),它并不影响我们的理解,现在可以先不用管,后面会有一篇文章专门介绍。
继续往后看,full_dsipatch_request
的代码如下:
|
|
这段代码最核心的内容是 dispatch_request
,加上请求的 hooks 处理和错误处理的内容。
NOTE:self.dispatch_request()
返回的是处理函数的返回结果(比如 hello world 例子中返回的字符串),finalize_request
会把它转换成 Response
对象。
在 dispatch_request
之前我们看到 preprocess_request
,之后看到 finalize_request
,它们里面包括了请求处理之前和处理之后的很多 hooks 。这些 hooks 包括:
- 第一次请求处理之前的 hook 函数,通过 before_first_request 定义
- 每个请求处理之前的 hook 函数,通过 before_request 定义
- 每个请求正常处理之后的 hook 函数,通过 after_request 定义
- 不管请求是否异常都要执行的 teardown_request hook 函数
dispatch_request
要做的就是找到我们的处理函数,并返回调用的结果,也就是路由的过程。我们下一篇文章来讲!