一个 Flask 应用运行过程剖析
WEB前端开发社区 昨天
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return "Hello World!" if __name__ == '__main__': app.run()
使用Flask框架开发的属于Web应用。由于Python使用WSGI网关,所以这个应用也可以叫WSGI应用; 服务器、Web应用的设计应该遵循网关接口的一些规范。对于WSGI网关,要求Web应用实现一个函数或者一个可调用对象webapp(environ, start_response)。服务器或网关中要定义start_response函数并且调用Web应用。关于这部分的内容可以参考:wsgiref包——符合WSGI标准的Web服务实现(一)。 Flask依赖于底层库werkzeug。相关内容可以参考:Werkzeug库简介。
@app.route('/') def index(): pass
def __call__(self, environ, start_response): """Shortcut for :attr:`wsgi_app`""" return self.wsgi_app(environ, start_response)
import os from werkzeug.wsgi import SharedDataMiddleware app = SharedDataMiddleware(app, { '/shared': os.path.join(os.path.dirname(__file__), 'shared') })
self.wsgi_app = SharedDataMiddleware(self.wsgi_app, { self.static_path: target })
from flask import Flask from werkzeug import DispatcherMiddleware app1 = Flask(__name__) app2 = Flask(__name__) app = Flask(__name__) @app1.route('/') def index(): return "This is app1!" @app2.route('/') def index(): return "This is app2!" @app.route('/') def index(): return "This is app!" app = DispatcherMiddleware(app, { '/app1': app1, '/app2': app2 }) if __name__ == '__main__': from werkzeug.serving import run_simple run_simple('localhost', 5000, app)
如果访问/,则会触发app(environ, start_response)(注意: 此时app是一个Flask对象),进而处理要访问app的请求; 如果访问/app1,则会触发app1(environ, start_response),进而处理要访问app1的请求。访问/app2同理。
def wsgi_app(environ, start_response): with self.request_context(environ): ...
# Flask v0.1 class _RequestContext(object): """The request context contains all request relevant information. It is created at the beginning of the request and pushed to the `_request_ctx_stack` and removed at the end of it. It will create the URL adapter and request object for the WSGI environment provided. """ def __init__(self, app, environ): self.app = app self.url_adapter = app.url_map.bind_to_environ(environ) self.request = app.request_class(environ) self.session = app.open_session(self.request) self.g = _RequestGlobals() self.flashes = None def __enter__(self): _request_ctx_stack.push(self) def __exit__(self, exc_type, exc_value, tb): # do not pop the request stack if we are in debug mode and an # exception happened. This will allow the debugger to still # access the request object in the interactive shell. if tb is None or not self.app.debug: _request_ctx_stack.pop()
app ——上下文对象的app属性是当前的Flask应用; url_adapter ——上下文对象的url_adapter属性是通过Flask应用中的Map实例构造成一个MapAdapter实例,主要功能是将请求中的URL和Map实例中的URL规则进行匹配; request ——上下文对象的request属性是通过Request类构造的实例,反映请求的信息; session ——上下文对象的session属性存储请求的会话信息; g ——上下文对象的g属性可以存储全局的一些变量。 flashes ——消息闪现的信息。
_request_ctx_stack = LocalStack()
{880: {'stack': [<flask._RequestContext object>]}, 13232: {'stack': [<flask._RequestContext object>]}}
current_app = LocalProxy(lambda: _request_ctx_stack.top.app) request = LocalProxy(lambda: _request_ctx_stack.top.request) session = LocalProxy(lambda: _request_ctx_stack.top.session) g = LocalProxy(lambda: _request_ctx_stack.top.g)
首先,会生成一个请求上下文对象,这个上下文对象包含请求相关的信息。并且在进入上下文环境时,LocalStack会将这个上下文对象推入栈结构中以存储这个对象; 在这个上下文环境中可以进行请求处理过程,这个稍后再介绍。不过可以以一种“全局”的方式访问上下文对象中的变量,例如app、request、session、g等; 当请求结束,退出上下文环境时,LocalStack会清理当前线程/协程产生的数据(请求上下文对象); Flask 0.1版本只有“请求上下文”的概念,在Flask 0.9版本中又增加了“应用上下文”的概念。关于“应用上下文”,以后再加以分析。
def wsgi_app(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)
在请求正式被处理之前的一些操作,调用preprocess_request()方法,例如打开一个数据库连接等操作; 正式处理请求。这个过程调用dispatch_request()方法,这个方法会根据URL匹配的情况调用相关的视图函数; 将从视图函数返回的值转变为一个Response对象; 在响应被发送到WSGI服务器之前,调用process_response(response)做一些后续处理过程; 调用response(environ, start_response)方法将响应发送回WSGI服务器。关于此方法的使用,可以参考:Werkzeug库——wrappers模块; 退出上下文环境时,LocalStack会清理当前线程/协程产生的数据(请求上下文对象)。
赞 (0)