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

flask是如何处理请求的

1.这次从上一篇文章Flask是如何运行起来的接着说。上一次提到了Flask的__call__方法,会在请求到来被调用。传入的参数为environstart_responseenviron其实就是请求头的一些参数,包括协议号、请求方法、请求路径等参数(可以在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

你可能感兴趣的:(flask,python,wsgi)