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