小辣椒開發日記

  • 2020 年 5 月 31 日
  • 筆記

小辣椒開發筆記

應用啟動流程

根據WSGI協議,應用程序會接收到web server提供的environ與start_response兩個參數,我們只需要從environ參數中提取相關信息並返回一個可迭代對象即可。

flask對該流程的處理邏輯如下:

def wsgi_app(self, environ, start_response):
    
    #根據提供的environ創建請求上下文對象,併入棧
    ctx = self.request_context(environ)
    ctx.push()
    error = None

    try:
        # 正確的請求處理路徑,會通過路由找到對應的處理函數
        response = self.full_dispatch_request()
    except Exception as e:
        # 錯誤處理,默認是 InternalServerError 錯誤處理函數,客戶端會看到服務器 500 異常
        error = e
        response = self.handle_exception(e)
    return response(environ, start_response)

首先對出入的environ參數創建一個RequestContext請求上下文對象,該對象包含着HTTP請求所含有的所有信息。之後請求上下文建立成功後,會執行response = self.full_dispatch_request(),該語句會根據請求中的路由找到相應的處理函數來處理請求,並將該函數的返回值處理後生成response對象返回。在處理請求的過程中,若遇到了異常,則該異常會被捕獲並交給handle_exception(e)函數處理,該函數接受一個異常對象並返回一個response對象。之後獲取response對象後,調用該對象即可。(所以說我們的應用程序好像更像是一個中間件?)

路由處理

    def dispatch_request(self):
        rule = request.url_rule
        return self.view_functions[rule.endpoint](**request.view_args)

由路由到函數的映射關係為路由->端點->函數,在werkzeug庫中已經提供了一系列的類供我們處理路由與端點的關係,而我們需要做的只是處理端點到函數的映射關係,該關係用一個字典即可輕鬆搞定。由代碼可知我們會在生成request對象的時候根據路由獲取對應的端點,在處理請求的時候我們只需根據端點調用相應函數即可。而對於端點與函數的映射關係,我們提供了一個簡單的函數用於註冊:

    def add_url_rule(self, path, view_func, endpoint=None, methods=None):
        if endpoint is None:
            endpoint = view_func.__name__

        if methods is None:
            methods = getattr(view_func, "methods", None) or ("GET",)

        rule = Rule(path, endpoint=endpoint, methods=methods)
        self.url_map.add(rule)
        self.view_functions[endpoint] = view_func

異常處理

    def handler_exceptions(self, e):
        exc_type = type(e)

        if exc_type in self.handler_map:
            handler = self.handler_map.get(exc_type)
            return handler(e)
        else:
            raise e

請求處理過程中觸發異常的話,異常會被捕獲並交給該函數處理,該函數接受一個異常後,根據異常所屬的類查看該異常的處理方式是否被註冊,若已被註冊,則將該異常拋給註冊的錯誤處理器處理,並返迴響應。若未被註冊,則將異常再次拋出,觸發程序錯誤。用於註冊錯誤處理器的函數如下:

    def register_error_handler(self, exc_class_or_code, handler):
        exc_class = _find_exceptions(exc_class_or_code)
        self.handler_map[exc_class] = handler