flask 是如何分發請求的?

這篇博客會涉及一些 WSGI 的知識,不了解的可以看這篇博客,簡單了解一下。

Python 的 WSGI 簡單入門

一、請求在 flask 中的處理過程

我們先來看一下 werkzeug.routing 包下 Map 和 Rule 方法的使用,這里給出一個官方的示例(我進行了一點修改并增加了簡單的運行代碼):

from werkzeug.routing import Map, Rule, Subdomain, NotFound, RequestRedirecturl_map = Map([Rule('/', endpoint='blog/index'),Rule('/<int:year>', endpoint='blog/archive'),Rule('/<int:year>/<int:month>/', endpoint='blog/archive'),Rule('/<int:year>/<int:month>/<int:day>', endpoint='blog/archive'),Rule('/<int:year>/<int:month>/<int:day>/<slug>', endpoint='blog/show_post'),Rule('/about', endpoint='blog/about_me'),Rule('/feeds', endpoint='blog/feeds'),Rule('/feeds/<feed_name>.rss', endpoint='blog/show_feed')
])def application(environ, start_response):urls = url_map.bind_to_environ(environ)try:endpoint, args = urls.match()except HTTPException as e:return e(environ, start_response)start_response('200 OK', [('Content-Type', 'text/plain')])rsp = f'Rule points to {endpoint!r} with arguments {args!r}'return [rsp.encode('utf-8')] # 直接返回字符串會報錯,這里進行一次轉換# provide a basic wsgi for test!
if __name__ == '__main__':from wsgiref.simple_server import make_serverwith make_server('', 8000, application) as httpd:print("Listening on port 8000....")httpd.serve_forever()

flask 的底層也是依賴于 Map 和 Rule,所以我們使用 @route 或者 add_url_rule 最終的目的也是構建類似上面的 url_map 對象,只不過它更加易用。有趣的是,這里并沒有 view_func 函數,所以我們是統一返回了 200 OK,不過 urls.match 的參數也表明了我們得到的是 endpoint,這是我們通過它來查找到對應 view_func 的關鍵信息。

要注意這里的 urls.match() 的返回值中這個 args 是指 url 中的定義的參數,下面是幾個示例:

urls = m.bind("example.com", "/")
urls.match("/", "GET")
# ('index', {})
urls.match("/downloads/42")
# ('downloads/show', {'id': 42})

1.1 add_url_rule 方法 和 @route 裝飾器

add_url_rule: Connects a URL rule. Works exactly like the :meth:route decorator. If a view_func is provided it will be registered with the endpoint.
連接 URL 規則。其工作原理與 route 裝飾器完全相同。如果提供了 view_func 函數,它會被用 endpoint 來注冊。

基礎示例:

@app.route('/')
def index():pass

等價于以下:

def index():passapp.add_url_rule('/', 'index', index)

如果沒有提供 view_func 函數,需要手動綁定 endpointview_func 函數。

app.view_function['index'] = index

在內部,route 方法會調用 add_url_rule 方法。

下面我們來看源碼,這里我進行了刪減,對于我認為不重要的部分去掉,我認為這樣可以節約理解設計思路的腦力。

@setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None,provide_automatic_options=None, **options):# 這里上下省略部分代碼,只保留我認為關鍵的代碼if endpoint is None:endpoint = _endpoint_from_view_func(view_func)rule = self.url_rule_class(rule, methods=methods, **options)self.url_map.add(rule)if view_func is not None:old_func = self.view_functions.get(endpoint)if old_func is not None and old_func != view_func:raise AssertionError('View function mapping is overwriting an ''existing endpoint function: %s' % endpoint)self.view_functions[endpoint] = view_func

說明:首先如果 endpoint 為空,則會使用 view_func 函數的名字,接著使用 add_url_rule 函數的參數創建 Rule 對象,將其加入 self.url_map 中,這是一個 Map 對象。然后會將 endpoint 作為鍵, view_func 作為值,存入 self.view_functions 中,它是一個 dict 對象。

也就是說我們最終得到了下面兩個對象,它們是 Flask 類的兩個實例屬性。還記得上面的 urls.match 方法嗎?當我們獲取到 endpoint 后,就可以它為鍵在 slef.view_functions 中去索引對應的 view_func 函數,然后用它來執行對應路由的請求。

class Flask(_PackageBoundObject):def __init__(self,import_name,static_url_path=None,static_folder='static',static_host=None,host_matching=False,subdomain_matching=False,template_folder='templates',instance_path=None,instance_relative_config=False,root_path=None):#: The :class:`~werkzeug.routing.Map` for this instance.self.url_map = Map()#: A dictionary of all view functions registered. The keys will #: be function names which are also used to generate URLs and #: the values are the function objects themselves.#: To register a view function, use the :meth:`route` decorator.self.view_functions = {}

route 只是一個方便的裝飾器函數,本質上還是調用 add_url_rule 函數。

def route(self, rule, **options):"""A decorator that is used to register a view function for agiven URL rule.  This does the same thing as :meth:`add_url_rule`but is intended for decorator usage::@app.route('/')def index():return 'Hello World'"""def decorator(f):endpoint = options.pop('endpoint', None)self.add_url_rule(rule, endpoint, f, **options)return freturn decorator

2.1 Flask 中的請求處理過程

我們創建的 Flask 的實例,最終也是類似于上面的 application 被 wsgi 服務調用,只是更加復雜一些,下面就來看看簡化的流程:

class Flask(_PackageBoundObject):def __call__(self, environ, start_response):"""The WSGI server calls the Flask application object as theWSGI application. This calls :meth:`wsgi_app` which can bewrapped to applying middleware."""return self.wsgi_app(environ, start_response)def wsgi_app(self, environ, start_response):"""The actual WSGI application. This is not implemented in:meth:`__call__` so that middlewares can be applied withoutlosing a reference to the app object. Instead of doing this::app = MyMiddleware(app)It's a better idea to do this instead::app.wsgi_app = MyMiddleware(app.wsgi_app)Then you still have the original application object around andcan continue to call methods on it."""ctx = self.request_context(environ)              # 創建請求上下文error = Nonetry:try:ctx.push()                               # 推入請求上下文response = self.full_dispatch_request()  # 分派請求except Exception as e:error = eresponse = self.handle_exception(e)except:error = sys.exc_info()[1]raisereturn response(environ, start_response)     # 響應客戶端finally:if self.should_ignore_error(error):error = Nonectx.auto_pop(error)                          # 彈出,防止積壓,造成資源泄漏def full_dispatch_request(self):"""Dispatches the request and on top of that performs requestpre and postprocessing as well as HTTP exception catching anderror handling... versionadded:: 0.7"""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 dispatch_request(self):"""Does the request dispatching.  Matches the URL and returns thereturn value of the view or error handler.  This does not have tobe a response object.  In order to convert the return value to aproper response object, call :func:`make_response`."""req = _request_ctx_stack.top.request        # 獲取當前請求的信息if req.routing_exception is not None:self.raise_routing_exception(req)rule = req.url_rule                         # 獲取到 url 對象# if we provide automatic options for this URL and the# request came with the OPTIONS method, reply automaticallyif getattr(rule, 'provide_automatic_options', False) \and req.method == 'OPTIONS':return self.make_default_options_response()             # 從 view_function 中找到endpoint對應的# otherwise dispatch to the handler for that endpoint       # view_func 函數,通過視圖參數調用它并返回結果,return self.view_functions[rule.endpoint](**req.view_args)  # 注意這里返回的并非響應對象。def finalize_request(self, rv, from_error_handler=False):"""Given the return value from a view function this finalizesthe request by converting it into a response and invoking thepostprocessing functions.  This is invoked for both normalrequest dispatching as well as error handlers.Because this means that it might be called as a result of afailure a special safe mode is available which can be enabledwith the `from_error_handler` flag.  If enabled, failures inresponse processing will be logged and otherwise ignored.:internal:"""response = self.make_response(rv)                   # 視圖函數的返回結果被傳入了這里,并轉化成響應對象try:                                                # 關于這個 response 對象,這里就不往下繼續了,下面response = self.process_response(response)      # 已經很抽象了,我覺得了解到這里即可。request_finished.send(self, response=response)except Exception:if not from_error_handler:raiseself.logger.exception('Request finalizing failed with an ''error while handling an error')return response

總結:flask 實例通過請求的 URL 來查找對應的 endpoint,再通過它來查找到對應的視圖函數 view_func,然后傳入視圖函數的參數進行請求處理。在調用視圖函數之前,它已經把請求上下文推入了,所以我們在視圖函數中可以自由的使用它們,這就是 flask 處理一個請求的大致過程。

關于請求上下文中的全局變量,也就是 request 這些的使用,可以閱讀這篇博客:

對 flask 框架中的全局變量 request 探究

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/70783.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/70783.shtml
英文地址,請注明出處:http://en.pswp.cn/web/70783.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

怎么獲取免費的 GPU 資源完成大語言模型(LLM)實驗

怎么獲取免費的 GPU 資源完成大語言模型(LLM)實驗 目錄 怎么獲取免費的 GPU 資源完成大語言模型(LLM)實驗在線平臺類Google ColabKaggle NotebooksHugging Face Spaces百度飛槳 AI Studio在線平臺類 Google Colab 特點:由 Google 提供的基于云端的 Jupyter 筆記本環境,提…

Python開發Django面試題及參考答案

目錄 Django 的請求生命周期是怎樣的? Django 的 MTV 架構中的各個組件分別是什么? Django 的 URL 路由是如何工作的? Django 的視圖函數和視圖類有什么區別? Django 的模板系統是如何渲染 HTML 的? Django 的 ORM 是如何工作的? Django 的中間件是什么?它的作用是…

【圖像的讀寫與基本操作】

圖像的讀寫與基本操作 目錄 圖像的讀寫與基本操作目標知識點1. 圖像的讀寫 &#xff1a;2. 圖像的縮放 &#xff1a;3. 圖像的翻轉 &#xff1a;4. 圖像的裁剪 &#xff1a;5. 顏色空間轉換 &#xff1a; 示例代碼1. 圖像的讀寫 &#xff1a;2. 圖像的縮放 &#xff1a;3. 圖像…

《數字圖像處理》筆記

文章目錄 第一章 緒論1.1 什么是數字圖像處理數字圖像的概念數字圖像的組成數字圖像處理的概念 1.4 數字圖像處理的基本步驟 第二章 數字圖像基礎2.2 光和電磁波譜可見光單色光灰度級發光強度光通量亮度 2.3 圖像感知和獲取將照射能量變換為數字圖像的傳感器簡單的圖像形成模型…

網絡安全掃描--基礎篇

前言 1、了解互聯網安全領域中日趨重要的掃描技術 2、了解在不同網絡場景下掃描技術手段 3、熟悉linux下系統內核防護策略并能大件一個有效的系統防護體系 4、增強工作安全意識&#xff0c;并能有效的實踐于工作場景中 目錄 1、熟悉主機掃描工具&#xff08;fping&#xff0c;…

前端防重復請求終極方案:從Loading地獄到精準攔截的架構升級

&#x1f525; 事故現場還原&#xff1a;瘋狂點擊引發的血案 凌晨1點23分&#xff0c;監控系統突然告警&#xff1a; &#x1f4c9; 服務器CPU飆升至98% &#x1f5c3;? 數據庫出現3000臟數據 &#x1f4a5; 用戶端彈出上百個錯誤彈窗 事故原因&#xff1a;黑產腳本通過0.5秒…

基于Spring Boot的供應商管理系統設計與實現(LW+源碼+講解)

專注于大學生項目實戰開發,講解,畢業答疑輔導&#xff0c;歡迎高校老師/同行前輩交流合作?。 技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;…

Redis|事務

文章目錄 是什么能干嘛Redis 事務 VS 數據庫事務怎么玩小總結 是什么 首先回想一下什么是數據庫的事務&#xff1f;數據庫事務是指作為單個邏輯單元執行的一系列操作&#xff0c;具備以下四個關鍵特性&#xff08;ACID&#xff09;&#xff1a; 原子性&#xff08;Atomicity&am…

一周學會Flask3 Python Web開發-Jinja2模板繼承和include標簽使用

鋒哥原創的Flask3 Python Web開發 Flask3視頻教程&#xff1a; 2025版 Flask3 Python web開發 視頻教程(無廢話版) 玩命更新中~_嗶哩嗶哩_bilibili 不管是開發網站還是后臺管理系統&#xff0c;我們頁面里多多少少有公共的模塊。比如博客網站&#xff0c;就有公共的頭部&…

二十三種設計模式詳解

二十三種設計模式是軟件開發中用于解決常見問題的經典解決方案&#xff0c;它們由 Erich Gamma 等四位作者在《設計模式&#xff1a;可復用面向對象軟件的基礎》一書中提出。這些模式分為三大類&#xff1a;創建型模式、結構型模式 和 行為型模式。 1. 創建型模式&#xff08;…

用pyside6創建一個界面并實現一個小功能且能打包成問題記錄

現在我們要開發一個程序&#xff0c;讓用戶輸入一段文本包含&#xff1a;員工姓名、薪資、年齡。該程序可以把薪資在 2萬 以上、以下的人員名單分別打印出來。 1用designer創建界面并生成UI文件&#xff1b; 2直接調用ui文件實現功能&#xff1b; from PySide6.QtWidgets im…

計算機畢業設計 ——jspssm510springboot 的人職匹配推薦系統

作者&#xff1a;程序媛9688 開發技術&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等。 &#x1f31f;文末獲取源碼數據庫&#x1f31f; 感興趣的可以先收藏起來&#xff0c;還有大家在畢設選題&#xff08;免費咨詢指導選題&#xff09;&#xf…

包子湊數——藍橋杯真題Python

包子湊數 輸入輸出樣例 示例 1 輸入 2 4 5輸出 6樣例說明 湊不出的數目包括&#xff1a;1, 2, 3, 6, 7, 11。 示例 2 輸入 2 4 6輸出 INF樣例說明 所有奇數都湊不出來&#xff0c;所以有無限多個 運行限制 最大運行時間&#xff1a;1s最大運行內存: 256M 最大公約數 最大公…

SSM和SpringBoot有什么區別?

SSM&#xff08;Spring、Spring MVC、MyBatis&#xff09;和 Spring Boot 有以下一些區別&#xff1a; 配置方式 SSM&#xff1a;配置相對繁瑣&#xff0c;需要在多個 XML 文件中進行大量的配置。Spring Boot&#xff1a;采用“約定大于配置”的原則&#xff0c;極大地簡化了配…

極簡Python服務器后端

在Python中&#xff0c;可以使用http.server模塊和json模塊來創建一個簡單的HTTP服務器&#xff0c;該服務器可以響應80端口上的/query POST請求&#xff0c;并且請求體為JSON格式。 需要注意&#xff0c;在Linux系統上&#xff0c;使用低于1024的端口&#xff08;如80端口&…

文檔檢索服務平臺

文檔檢索服務平臺是基于Elasticsearch的全文檢索&#xff0c;包含數據采集、數據清洗、數據轉換、數據檢索等模塊。 項目地址&#xff1a;Github、國內Gitee 演示地址&#xff1a;http://silianpan.cn/gdss/ 以下是演示角色和賬號&#xff08;密碼同賬號&#xff09;&#xf…

關于Postman自動獲取token

在使用postman測試聯調接口時&#xff0c;可能每個接口都需要使用此接口生成的令牌做Authorization的Bearer Token驗證&#xff0c;最直接的辦法可能會是一步一步的點擊&#xff0c;如下圖&#xff1a; 在Authorization中去選擇Bearer Token&#xff0c;然后將獲取到的token粘貼…

清華大學DeepSeek文檔下載,清華大學deepseek下載(完成版下載)

文章目錄 前言一、清華大學DeepSeek使用手冊下載二、清華大學DeepSeek使用手冊思維導圖 前言 這是一篇關于清華大學deepseek使用手冊pdf的介紹性文章&#xff0c;主要介紹了DeepSeek的定義、功能、使用方法以及如何通過提示語設計優化AI性能。以下是對這些核心內容的簡要概述&…

Linux:(3)

一&#xff1a;Linux和Linux互傳&#xff08;壓縮包&#xff09; scp:Linux scp 命令用于 Linux 之間復制文件和目錄。 scp 是 secure copy 的縮寫, scp 是 linux 系統下基于 ssh 登陸進行安全的遠程文件拷貝命令。 scp 是加密的&#xff0c;rcp 是不加密的&#xff0c;scp 是…

【新人系列】Python 入門專欄合集

? 個人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4dd; 專欄地址&#xff1a;https://blog.csdn.net/newin2020/category_12801353.html &#x1f4e3; 專欄定位&#xff1a;為 0 基礎剛入門 Python 的小伙伴提供詳細的講解&#xff0c;也歡迎大佬們…