综合编程

Flask源码分析(二)flask是如何处理请求的

微信扫一扫,分享到朋友圈

Flask源码分析(二)flask是如何处理请求的

flask是如何处理请求的

1.这次从上一篇文章 Flask是如何运行起来的
接着说。上一次提到了Flask__call__
方法,会在请求到来被调用。传入的参数为 environ
start_response
environ
其实就是请求头的一些参数,包括协议号、请求方法、请求路径等参数(可以在WSGIRequestHandler的 make_response
方法中查看)。而 start_response
即是对响应头的处理函数,这里是传入了这个函数的引用。

class Flask(_PackageBoundObject):
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
class WSGIRequestHandler(BaseHTTPRequestHandler, object):
def make_environ(self):
request_url = url_parse(self.path)
environ = {
"wsgi.version": (1, 0),
"wsgi.url_scheme": url_scheme,
"wsgi.input": self.rfile,
"wsgi.errors": sys.stderr,
"wsgi.multithread": self.server.multithread,
"wsgi.multiprocess": self.server.multiprocess,
"wsgi.run_once": False,
"werkzeug.server.shutdown": shutdown_server,
"SERVER_SOFTWARE": self.server_version,
"REQUEST_METHOD": self.command,
"SCRIPT_NAME": "",
"PATH_INFO": wsgi_encoding_dance(path_info),
"QUERY_STRING": wsgi_encoding_dance(request_url.query),
# Non-standard, added by mod_wsgi, uWSGI
"REQUEST_URI": wsgi_encoding_dance(self.path),
# Non-standard, added by gunicorn
"RAW_URI": wsgi_encoding_dance(self.path),
"REMOTE_ADDR": self.address_string(),
"REMOTE_PORT": self.port_integer(),
"SERVER_NAME": self.server.server_address[0],
"SERVER_PORT": str(self.server.server_address[1]),
"SERVER_PROTOCOL": self.request_version,
}
return environ
def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
if headers_sent:
reraise(*exc_info)
finally:
exc_info = None
elif headers_set:
raise AssertionError("Headers already set")
headers_set[:] = [status, response_headers]
return write

2.来看 __call__
方法接收这两个参数后执行了什么。 __call__
返回了自身的wsgi_app方法,所有对请求的处理,都在这个方法里了。 self.request_context
是创建当前请求的上下文环境,下一篇文章再详细讲吧。关键在 self.full_dispatch_request
方法,这里深挖一下。

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)

3. self.full_dispatch_request
*就是调度请求,并在其之上执行请求的预处理和后处理以及HTTP异常捕获和错误处理,实际就是执行相应的视图函数。在此深挖,可以看出其中的执行流程。

请求到来首先进行预处理,即进入视图函数之前,先执行 @app.before_first_request
@app.before_request
等装饰器内的代码。还可以看出,如果有多个预处理函数的话,如果第一个有返回值,那么只执行第一个,即比较靠上的那一个,也不执行与请求url相应的视图函数了。

预处理结束,会将返回的内容交给 self.make_response
这个方法进行处理,构建出返回的内容。然后是后处理,后处理会执行所有被 @after_request
装饰的后处理函数,而且每个后处理函数必须接受 response
参数,并返回,因为在这里 response
是被层层处理的。而且执行顺序和预处理是相反的,自下而上。

def full_dispatch_request(self):
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)  # 后处理
def preprocess_request(self):
'''预处理'''
bp = _request_ctx_stack.top.request.blueprint
funcs = self.url_value_preprocessors.get(None, ())
if bp is not None and bp in self.url_value_preprocessors:
funcs = chain(funcs, self.url_value_preprocessors[bp])
for func in funcs:
func(request.endpoint, request.view_args)
funcs = self.before_request_funcs.get(None, ())
if bp is not None and bp in self.before_request_funcs:
funcs = chain(funcs, self.before_request_funcs[bp])
for func in funcs:
rv = func()
if rv is not None:
return rv
def finalize_request(self, rv, from_error_handler=False):
'''后处理并返回response'''
response = self.make_response(rv)  #这里涉及到调用start_response那个函数了
try:
response = self.process_response(response)  # 后处理
request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception(
"Request finalizing failed with an error while handling an error"
)
return response
def process_response(self, response):
'''后处理'''
ctx = _request_ctx_stack.top
bp = ctx.request.blueprint
funcs = ctx._after_request_functions
if bp is not None and bp in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
if None in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[None]))
for handler in funcs:
response = handler(response)
if not self.session_interface.is_null_session(ctx.session):
self.session_interface.save_session(self, ctx.session, response)
return response

4.以上就是flask的请求处理逻辑了,那么最开始传入的start_response函数是在哪里被调用的呢?答案是上面提到的Flask中定义的这个wigi_app方法。这个方法中有一句: return response(environ, start_response)
。这个response实际就是BaseResponse类实例化的对象,而对象+()正是调用了它的__call__方法,然后到它的__call__方法中去看,果然,最终在这里调用了start_response。

class Flask(_PackageBoundObject):
# ...
# response_class即Response这个类
response_class = Response
# ...
def make_response(self, rv):
# ...
# make sure the body is an instance of the response class
if not isinstance(rv, self.response_class):
if isinstance(rv, (text_type, bytes, bytearray)):
# let the response class set the status and headers instead of
# waiting to do it manually, so that the class can handle any
# special logic
# 这里返回了response_class类的实例化对象
rv = self.response_class(rv, status=status, headers=headers)
status = headers = None
# ...
return rv
class Response(ResponseBase, JSONMixin):
"""Response继承自ResponseBase,继续深挖"""
pass
class Response(
BaseResponse,
ETagResponseMixin,
WWWAuthenticateMixin,
CORSResponseMixin,
ResponseStreamMixin,
CommonResponseDescriptorsMixin,
):
# Response又继承自BaseResponse
pass
class BaseResponse(object):
def __init__(
self,
response=None,
status=None,
headers=None,
mimetype=None,
content_type=None,
direct_passthrough=False,
):
pass
def __call__(self, environ, start_response):
# 果然,在这里!破案了
app_iter, status, headers = self.get_wsgi_response(environ)
start_response(status, headers)
return app_iter

高新增高留存的背后,是“从乱到治”的认知升级

上一篇

Creating a dynamic application with LoopBack

下一篇

你也可能喜欢

评论已经被关闭。

插入图片

热门栏目

Flask源码分析(二)flask是如何处理请求的

长按储存图像,分享给朋友