文章属于做者原创,原文发布在我的博客。python
全部的 python web 框架都要遵循 WSGI 协议,若是对 WSGI 不清楚,能够查看我以前的介绍文章。nginx
在这里仍是要简单回顾一下 WSGI 的核心概念。web
WSGI 中有一个很是重要的概念:每一个 python web 应用都是一个可调用(callable)的对象。在 flask 中,这个对象就是 app = Flask(__name__)
建立出来的 app
,就是下图中的绿色 Application 部分。要运行 web 应用,必须有 web server,好比咱们熟悉的 apache、nginx ,或者 python 中的 gunicorn ,咱们下面要讲到的 werkzeug
提供的 WSGIServer
,它们是下图的黄色 Server 部分。apache
NOTE: 图片来源。flask
Server 和 Application 之间怎么通讯,就是 WSGI 的功能。它规定了 app(environ, start_response)
的接口,server 会调用 application,并传给它两个参数:environ
包含了请求的全部信息,start_response
是 application 处理完以后须要调用的函数,参数是状态码、响应头部还有错误信息。服务器
WSGI application 很是重要的特色是:它是能够嵌套的。换句话说,我能够写个 application,它作的事情就是调用另一个 application,而后再返回(相似一个 proxy)。通常来讲,嵌套的最后一层是业务应用,中间就是 middleware。这样的好处是,能够解耦业务逻辑和其余功能,好比限流、认证、序列化等都实现成不一样的中间层,不一样的中间层和业务逻辑是不相关的,能够独立维护;并且用户也能够动态地组合不一样的中间层来知足不一样的需求。app
WSGI 的内容就讲这么多,咱们来看看 flask 的 hello world 应用:框架
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run()
这里的 app = Flask(__name__)
就是上面提到的 Application 部分,可是咱们并无看到 Server 的部分,那么它必定是隐藏到 app.run()
内部某个地方了。函数
应用启动的代码是 app.run()
,这个方法的代码以下:post
def run(self, host=None, port=None, debug=None, **options): """Runs the application on a local development server.""" from werkzeug.serving import run_simple # 若是host 和 port 没有指定,设置 host 和 port 的默认值 127.0.0.1 和 5000 if host is None: host = '127.0.0.1' if port is None: server_name = self.config['SERVER_NAME'] if server_name and ':' in server_name: port = int(server_name.rsplit(':', 1)[1]) else: port = 5000 # 调用 werkzeug.serving 模块的 run_simple 函数,传入收到的参数 # 注意第三个参数传进去的是 self,也就是要执行的 web application try: run_simple(host, port, self, **options) finally: self._got_first_request = False
NOTE:为了阅读方便,我删除了注释和不相干的部分,下面全部的代码都会作相似的处理,再也不赘述。
这个方法的内容很是简单:处理一下参数,而后调用 werkzeug
的 run_simple
。须要注意的是:run_simple
的第三个参数是 self
,也就是咱们建立的 Flask()
application。由于 WSGI server 不是文章的重点,因此咱们就不深刻讲解了。如今只须要知道它的功能就行:监听在指定的端口,收到 HTTP 请求的时候解析为 WSGI 格式,而后调用 app
去执行处理的逻辑。对应的执行逻辑在 werkzeug.serving:WSGIRequestHandler
的 run_wsgi
中有这么一段代码:
def execute(app): application_iter = app(environ, start_response) try: for data in application_iter: write(data) if not headers_sent: write(b'') finally: if hasattr(application_iter, 'close'): application_iter.close() application_iter = None
能够看到 application_iter = app(environ, start_response)
就是调用代码获取结果的地方。
要调用 app
实例,那么它就须要定义了 __call__
方法,咱们找到 flask.app:Flask
对应的内容:
def __call__(self, environ, start_response): """Shortcut for :attr:`wsgi_app`.""" return self.wsgi_app(environ, start_response) def wsgi_app(self, environ, start_response): """The actual WSGI application. """ # 建立请求上下文,并把它压栈。这个在后面会详细解释 ctx = self.request_context(environ) ctx.push() error = None try: try: # 正确的请求处理路径,会经过路由找到对应的处理函数 response = self.full_dispatch_request() except Exception as e: # 错误处理,默认是 InternalServerError 错误处理函数,客户端会看到服务器 500 异常 error = e response = self.handle_exception(e) return response(environ, start_response) finally: if self.should_ignore_error(error): error = None # 无论处理是否发生异常,都须要把栈中的请求 pop 出来 ctx.auto_pop(error)
上面这段代码只有一个目的:找处处理函数,而后调用它。除了异常处理以外,咱们还看到了 context
相关的内容(开始有 ctx.push()
,最后有 ctx.auto_pop()
的逻辑),它并不影响咱们的理解,如今能够先不用管,后面会有一篇文章专门介绍。
继续日后看,full_dsipatch_request
的代码以下:
def full_dispatch_request(self): """Dispatches the request and on top of that performs request pre and postprocessing as well as HTTP exception catching and error handling. """ self.try_trigger_before_first_request_functions() try: request_started.send(self) rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() except Exception as e: rv = self.handle_user_exception(e) return self.finalize_request(rv)
这段代码最核心的内容是 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
要作的就是找到咱们的处理函数,并返回调用的结果,也就是路由的过程。咱们下一篇文章来说!