Flask源碼分析(二)flask是如何處理請求的

flask是如何處理請求的

1.此次從上一篇文章Flask是如何運行起來的接着說。上一次提到了Flask的__call__方法,會在請求到來被調用。傳入的參數爲environstart_responseenviron其實就是請求頭的一些參數,包括協議號、請求方法、請求路徑等參數(能夠在WSGIRequestHandler的make_response方法中查看)。而start_response便是對響應頭的處理函數,這裏是傳入了這個函數的引用。python

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方法,這裏深挖一下。flask

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異常捕獲和錯誤處理,實際就是執行相應的視圖函數。在此深挖,能夠看出其中的執行流程。segmentfault

請求到來首先進行預處理,即進入視圖函數以前,先執行@app.before_first_request@app.before_request等裝飾器內的代碼。還能夠看出,若是有多個預處理函數的話,若是第一個有返回值,那麼只執行第一個,即比較靠上的那一個,也不執行與請求url相應的視圖函數了。session

預處理結束,會將返回的內容交給self.make_response這個方法進行處理,構建出返回的內容。而後是後處理,後處理會執行全部被@after_request裝飾的後處理函數,並且每一個後處理函數必須接受response參數,並返回,由於在這裏response是被層層處理的。並且執行順序和預處理是相反的,自下而上。app

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
相關文章
相關標籤/搜索