當前位置:首頁 » 網頁前端 » pythonweb項目部署
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

pythonweb項目部署

發布時間: 2023-06-14 16:44:03

1. python web 怎麼部署

學過PHP的都了解,php的正式環境部署非常簡單,改幾個文件就OK,用FastCgi方式也是分分鍾的事情。相比起來,Python在web應用上的部署就繁雜的多,主要是工具繁多,主流伺服器支持不足,在了解Python的生產環境部署方式之前,先明確一些概念!很重要!

CGI:

CGI即通用網關介面(Common Gateway Interface),是外部應用程序(CGI程序)與Web伺服器之間的介面標准,是在CGI程序和Web伺服器之間傳遞信息的規程。CGI規范允許Web伺服器執行外部程序,並將它們的輸出發送給Web瀏覽器,CGI將Web的一組簡單的靜態超媒體文檔變成一個完整的新的互動式媒體。通俗的講CGI就像是一座橋,把網頁和WEB伺服器中的執行程序連接起來,它把HTML接收的指令傳遞給伺服器的執行程序,再把伺服器執行程序的結果返還給HTML頁。CGI的跨平台性能極佳,幾乎可以在任何操作系統上實現。

CGI方式在遇到連接請求(用戶請求)先要創建cgi的子進程,激活一個CGI進程,然後處理請求,處理完後結束這個子進程。這就是fork-and-execute模式。所以用cgi方式的伺服器有多少連接請求就會有多少cgi子進程,子進程反復載入是cgi性能低下的主要原因。當用戶請求數量非常多時,會大量擠占系統的資源如內存,CPU時間等,造成效能低下。

CGI腳本工作流程:

  • 瀏覽器通過HTML表單或超鏈接請求指向一個CGI應用程序的URL。

  • 伺服器執行務器收發到請求。所指定的CGI應用程序。

  • CGI應用程序執行所需要的操作,通常是基於瀏覽者輸入的內容。

  • CGI應用程序把結果格式化為網路伺服器和瀏覽器能夠理解的文檔(通常是HTML網頁)。

  • 網路伺服器把結果返回到瀏覽器中。

  • python有cgi模塊可支持原生cgi程序

    FastCGI:

    FastCGI是一個可伸縮地、高速地在HTTP server和動態腳本語言間通信的介面。多數流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等,同時,FastCGI也被許多腳本語言所支持,其中就有Python。FastCGI是從CGI發展改進而來的。傳統CGI介面方式的主要缺點是性能很差,因為每次HTTP伺服器遇到動態程序時都需要重新啟動腳本解析器來執行解析,然後結果被返回給HTTP伺服器。這在處理高並發訪問時,幾乎是不可用的。FastCGI像是一個常駐(long-live)型的CGI,它可以一直執行著,只要激活後,不會每次都要花費時間去fork一次(這是CGI最為人詬病的fork-and-execute 模式)。CGI 就是所謂的短生存期應用程序,FastCGI 就是所謂的長生存期應用程序。由於 FastCGI 程序並不需要不斷的產生新進程,可以大大降低伺服器的壓力並且產生較高的應用效率。它的速度效率最少要比CGI 技術提高 5 倍以上。它還支持分布式的運算, 即 FastCGI 程序可以在網站伺服器以外的主機上執行並且接受來自其它網站伺服器來的請求。

    FastCGI是語言無關的、可伸縮架構的CGI開放擴展,其主要行為是將CGI解釋器進程保持在內存中並因此獲得較高的性能。眾所周知,CGI解釋器的反復載入是CGI性能低下的主要原因,如果CGI解釋器保持在內存中並接受FastCGI進程管理器調度,則可以提供良好的性能、伸縮性、Fail-Over特性等等。FastCGI介面方式採用C/S結構,可以將HTTP伺服器和腳本解析伺服器分開,同時在腳本解析伺服器上啟動一個或者多個腳本解析守護進程。當HTTP伺服器每次遇到動態程序時,可以將其直接交付給FastCGI進程來執行,然後將得到的結果返回給瀏覽器。這種方式可以讓HTTP伺服器專一地處理靜態請求或者將動態腳本伺服器的結果返回給客戶端,這在很大程度上提高了整個應用系統的性能。

    FastCGI的工作流程:

  • Web Server啟動時載入FastCGI進程管理器(PHP-CGI或者PHP-FPM或者spawn-cgi)

  • FastCGI進程管理器自身初始化,啟動多個CGI解釋器進程(可見多個php-cgi)並等待來自Web Server的連接。

  • 當客戶端請求到達Web Server時,FastCGI進程管理器選擇並連接到一個CGI解釋器。Web server將CGI環境變數和標准輸入發送到FastCGI子進程php-cgi。

  • FastCGI子進程完成處理後將標准輸出和錯誤信息從同一連接返回Web Server。當FastCGI子進程關閉連接時,請求便告處理完成。FastCGI子進程接著等待並處理來自FastCGI進程管理器(運行在Web Server中)的下一個連接。 在CGI模式中,php-cgi在此便退出。

  • FastCGI 的特點:

  • 打破傳統頁面處理技術。傳統的頁面處理技術,程序必須與 Web 伺服器或 Application 伺服器處於同一台伺服器中。這種歷史已經早N年被FastCGI技術所打破,FastCGI技術的應用程序可以被安裝在伺服器群中的任何一台伺服器,而通過 TCP/IP 協議與 Web 伺服器通訊,這樣做既適合開發大型分布式 Web 群,也適合高效資料庫控制。

  • 明確的請求模式。CGI 技術沒有一個明確的角色,在 FastCGI 程序中,程序被賦予明確的角色(響應器角色、認證器角色、過濾器角色)。

  • WSGI:

    PythonWeb伺服器網關介面(Python Web Server Gateway Interface,縮寫為WSGI)是為Python語言定義的Web伺服器和Web應用程序或框架之間的一種簡單而通用的介面。自從WSGI被開發出來以後,許多其它語言中也出現了類似介面。WSGI是作為Web伺服器與Web應用程序或應用框架之間的一種低級別的介面,以提升可移植Web應用開發的共同點。WSGI是基於現存的CGI標准而設計的。

    WSGI區分為兩個部份:一為「伺服器」或「網關」,另一為「應用程序」或「應用框架」。在處理一個WSGI請求時,伺服器會為應用程序提供環境上下文及一個回調函數(Callback Function)。當應用程序完成處理請求後,透過先前的回調函數,將結果回傳給伺服器。所謂的 WSGI 中間件同時實現了API的兩方,因此可以在WSGI服務和WSGI應用之間起調解作用:從WSGI伺服器的角度來說,中間件扮演應用程序,而從應用程序的角度來說,中間件扮演伺服器。「中間件」組件可以執行以下功能:

  • 重寫環境變數後,根據目標URL,將請求消息路由到不同的應用對象。

  • 允許在一個進程中同時運行多個應用程序或應用框架。

  • 負載均衡和遠程處理,通過在網路上轉發請求和響應消息。

  • 進行內容後處理,例如應用XSLT樣式表。

  • 以前,如何選擇合適的Web應用程序框架成為困擾Python初學者的一個問題,這是因為,一般而言,Web應用框架的選擇將限制可用的Web伺服器的選擇,反之亦然。那時的Python應用程序通常是為CGI,FastCGI,mod_python中的一個而設計,甚至是為特定Web伺服器的自定義的API介面而設計的。WSGI沒有官方的實現, 因為WSGI更像一個協議。只要遵照這些協議,WSGI應用(Application)都可以在任何伺服器(Server)上運行, 反之亦然。WSGI就是Python的CGI包裝,相對於Fastcgi是PHP的CGI包裝。

    WSGI將 web 組件分為三類: web伺服器,web中間件,web應用程序, wsgi基本處理模式為 : WSGI Server -> (WSGI Middleware)* -> WSGI Application 。

    uwsgi:

    uwsgi協議是一個uWSGI伺服器自有的協議,它用於定義傳輸信息的類型(type of information),每一個uwsgi packet前4byte為傳輸信息類型描述,它與WSGI相比是兩樣東西。據稱其效率是fcgi的10倍。具體的協議內容請參考:the uwsgi protocol

    以上四者都可以理解為協議!協議!協議!實現了這樣的協議,就可以實現Web伺服器與Web應用程序相關聯的web服務!

    uWSGI:

    uWSGI項目旨在為部署分布式集群的網路應用開發一套完整的解決方案。uWSGI主要面向web及其標准服務,已經成功的應用於多種不同的語言。由於uWSGI的可擴展架構,它能夠被無限制的擴展用來支持更多的平台和語言。目前,你可以使用C,C++和Objective-C來編寫插件。項目名稱中的「WSGI」是為了向同名的Python Web標准表示感謝,因為WSGI為該項目開發了第一個插件。uWSGI是一個Web伺服器,它實現了WSGI協議、uwsgi、http等協議。uWSGI,既不用wsgi協議也不用FastCGI協議,而是自創了上文說將的uwsgi協議。

    uWSGI的主要特點如下:

  • 超快的性能。

  • 低內存佔用(實測為apache2的mod_wsgi的一半左右)。

  • 多app管理。

  • 詳盡的日誌功能(可以用來分析app性能和瓶頸)。

  • 高度可定製(內存大小限制,服務一定次數後重啟等)。

  • Gunicorn:

    和uWSGi類似的工具,從rails的部署工具(Unicorn)移植過來的。但是它使用的協議是前文所講的WSGI,這是python2.5時定義的官方標准(PEP 333),根紅苗正,而且部署比較簡單,詳細的使用教程請點擊這里。Gunicorn採用prefork模式,Gunicorn 伺服器與各種 Web 框架兼容,只需非常簡單的執行,輕量級的資源消耗,以及相當迅速。它的特點是與 Django 結合緊密,部署特別方便。 缺點也很多,不支持 HTTP 1.1,並發訪問性能不高,與 uWSGI,Gevent 等有一定的性能差距。

    1. Gunicorn設計

    Gunicorn 是一個 master進程,spawn 出數個工作進程的 web 伺服器。master 進程式控制制工作進程的產生與消亡,工作進程只需要接受請求並且處理。這樣分離的方式使得 reload 代碼非常方便,也很容易增加或減少工作進程。 工作進程這塊作者給了很大的擴展餘地,它可以支持不同的IO方式,如 Gevent,Sync 同步進程,Asyc 非同步進程,Eventlet 等等。master 跟 worker 進程完全分離,使得 Gunicorn 實質上就是一個控制進程的服務。

    2. Gunicorn源碼結構

    從 Application.run() 開始,首先初始化配置,從文件讀取,終端讀取等等方式完成 configurate。然後啟動 Arbiter,Arbiter 是實質上的 master 進程的核心,它首先從配置類中讀取並設置,然後初始化信號處理函數,建立 socket。然後就是開始 spawn 工作進程,根據配置的工作進程數進行 spawn。然後就進入了輪詢狀態,收到信號,處理信號然後繼續。這里喚醒進程的方式是建立一個 PIPE,通過信號處理函數往 pipe 里 write,然後 master 從 select.select() 中喚醒。

    工作進程在 spawn 後,開始初始化,然後同樣對信號進行處理,並且開始輪詢,處理 HTTP 請求,調用 WSGI 的應用端,得到 resopnse 返回。然後繼續。

    Sync 同步進程的好處在於每個 request 都是分離的,每個 request 失敗都不會影響其他 request,但這樣導致了性能上的瓶頸。

    Tornado:

    Tornado即使一款python 的開發框架,也是一個非同步非阻塞的http伺服器,它本身的數據產出實現沒有遵從上文所說的一些通用協議,因為自身就是web伺服器,所以動態請求就直接通過內部的機制,輸出成用戶所請求的動態內容。如果把它作為一個單獨伺服器,想用它來配合其他的框架如Flask來部署,則需要採用WSGI協議,Tornado內置了該協議,tornado.wsgi.WSGIContainer。

    wsgiref:

    Python自帶的實現了WSGI協議的的wsgi server。wsgi server可以理解為一個符合wsgi規范的web server,接收request請求,封裝一系列環境變數,按照wsgi規范調用注冊的wsgi app,最後將response返回給客戶端。Django的自帶伺服器就是它了。

    以上都可以理解為實現!實現!實現!實現了協議的工具!

    註:mod_wsgi(apache的模塊)其實也是實現了wsgi協議的一個模塊,現在幾乎不廢棄了,所以也不多說了,感興趣的自己查一下吧。

    所以如果你採用Django框架開發了應用之後,想部署到生產環境,肯定不能用Django自帶的,可以用使用uwsgi協議的uWSGI伺服器,也可以採用實現了WSGI協議的gunicorn或者Tornado,亦可以用FastCGI、CGI模式的Nginx、lighttpd、apache伺服器。其他框架亦如此!明白了這些概念在部署的時候就可以做到心中有數,各種工具之間的搭配也就「知其然,並知其所以然」了。

    在我們組的項目中有兩種框架Django和Tornado,生產環境也用到了兩種部署方式。uWSGI和Gunicorn:

    Django項目用Nginx+uWSGI方式部署,Tornado項目用Nginx+Gunicorn方式部署:

    Nginx都作為負載均衡以及靜態內容轉發。Tornado項目用supervisord來管理Gunicorn,用Gunicorn管理Tornado。眾所周知,由於Python的GIL存在,所以Python的並發都採用多進程模式,所以我們部署的方式是一個核心兩個進程。

2. 如何用python和web.py搭建一個網站

一、使用工具:python、web.py

二、搭建步驟:

1、 環境搭建。

安裝python2.7.10,注意要把python路徑加入系統環境變數。版本不能低於2.7.9,但不能用3.0以上,web.py支持不好。安裝web.py, 官方網下載來裝就行,記得是解壓後進去:python setup.py install,安裝wingIDE,這個是最好用的python編輯器,裝apache並配置python-wscgi,

2. 開發。

建立資料庫建議寫個生成腳本,比如createDataBase.py,有改動重新運行一遍,不要試用ide去建。

三、注意事項:注意要把python路徑加入系統環境變數。版本不能低於2.7.9,但不能用3.0以上,web.py支持不好。

3. linux下python和webpy怎麼搭建

基於python的web開發,這里我們使用linux為開發環境,搭建基於nginx + web.py + fastcgi
有些基本基本概念解釋下,哈哈,因為我不懂
1.wsgi為Web伺服器網關介面(Python Web Server Gateway Interface,縮寫為WSGI)是是為Python語言定義的Web伺服器和Web應用程序或框架之間的一種簡單而通用的介面。自從WSGI被開發出來以後,許多其它語言中也出現了類似介面
2.uwsgi,另一種python定義的web伺服器和web應用的介面
3.REST服務,REST(Representational State Transfer表述性狀態轉移)是一種針對網路應用的設計和開發方式,可以降低開發的復雜性,提高系統的可伸縮性。
4.CRUD是指在做計算處理時的增加(Create)、查詢(Retrieve)(重新得到數據)、更新(Update)和刪除(Delete)幾個單詞的首字母簡寫。主要被用在描述軟體系統中資料庫或者持久層的基本操作功能

以下內容主要來自
http://webpy.org/cookbook/fastcgi-nginx
需要的軟體
nginx 0.7以上版本,我使用的是nginx 0.9.2
webpy我使用的web.py-0.37
spawn-fcgi 1.6.3
flup 1.0
nginx的配置請參看官方文檔
spawn-fcgi是lighttpd的一個子項目用於多進程管理
webpy和flup安裝方式為解壓後運行python setup.py install
安裝編寫index.py

點擊(此處)折疊或打開
#!/usr/bin/env python

# -*- coding: utf-8 -*-

import web

urls = ("/.*", "hello")

app = web.application(urls, globals())

class hello:

def GET(self):

return 'Hello, world!'

if __name__ == "__main__":

web.wsgi.runwsgi = lambda func, addr=None: web.wsgi.runfcgi(func, addr)

app.run()
注意index.py需要使用命令chmod +x index.py加入可執行許可權
將index.py放入/data/www(我所使用的目錄你可以修改)
修改nginx.conf配置
index要加入index.py
Nginx的配置加入

點擊(此處)折疊或打開
location / {

fastcgi_param REQUEST_METHOD $request_method;

fastcgi_param QUERY_STRING $query_string;

fastcgi_param CONTENT_TYPE $content_type;

fastcgi_param CONTENT_LENGTH $content_length;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;

fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

fastcgi_param REMOTE_ADDR $remote_addr;

fastcgi_param REMOTE_PORT $remote_port;

fastcgi_param SERVER_ADDR $server_addr;

fastcgi_param SERVER_PORT $server_port;

fastcgi_param SERVER_NAME $server_name;

fastcgi_param SERVER_PROTOCOL $server_protocol;

fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;

fastcgi_param PATH_INFO $fastcgi_script_name;

fastcgi_pass 127.0.0.1:9002;

}
使用Spawn-fcgi
spawn-fcgi -d /data/www -f /data/www/index.py -a 127.0.0.1 -p 9002
如果報錯為126,說明index.py沒有可執行許可權
netstat -lnp | grep 9002參考是否啟動成功
我運行的實際為
spawn-fcgi -d /data/www -f /data/www/index.py -a 127.0.0.1 -p 9002 -F 2
啟動2個進程
啟動nginx
瀏覽器輸入地址

成功結束

4. Python的web項目如何進行動態重載和熱部署

真正意義上的代碼熱部署應該是類似erlang那樣的,將代碼更新到節點後不停服務,不斷連接的自動應用新代碼。auto reload什麼的還是會造成業務瞬間中斷。我感覺是可以從wsgi容器級別上實現,比如更新代碼後檢測到文件變更,然後通知容器創建新的wsgi application的實例,之後所有新的請求都發送到新的wdgi application實例上。等舊wsgi application實例的最後一個請求返回後就將其回收掉。不過貌似沒有看到類似的實現

5. 純 Python 寫一個 Web 框架,就是這么簡單

造輪子是最好的一種學習方式,本文嘗試從0開始造個Python Web框架的輪子,我稱它為 ToyWebF 。

本文操作環境為:MacOS,文中涉及的命令,請根據自己的系統進行替換。

ToyWebF的簡單特性:

下面我們來實現這些特性。

首先,我們需要安裝gunicorn,回憶一下Flask框架,該框架有內置的Web伺服器,但不穩定,所以上線時通常會替換成uWSGI或gunicorn,這里不搞這個內置Web服務,直接使用gunicorn。

我們創建新的目錄與Python虛擬環境,在該虛擬環境中安裝gunicorn

在啥都沒有的情況下,構建最簡單的Web服務,在ToyWebF目錄下,創建app.py與api.py文件,寫入下面代碼。

運行 gunicorn app:app 訪問 http://127.0.0.1:8000 ,可以看見 Hello, World! ,但現在請求體中的參數在environ變數中,難以解析,我們返回的response也是bytes形式。

我們可以使用webob庫,將environ中的數據轉為Request對象,將需要返回的數據轉為Response對象,處理起來更加直觀方便,直接通過pip安裝一下。

然後修改一下API類的 __call__方法 ,代碼如下。

上述代碼中,通過webob庫的Request類將environ對象(請求的環境信息)轉為容易處理的request,隨後調用handle_request方法對request進行處理,處理的結果,通過response對象返回。

handle_request方法在ToyWebF中非常重要,它會匹配出某個路由對應的處理方法,然後調用該方法處理請求並將處理的結果返回,在解析handle_request前,需要先討論路由注冊實現,代碼如下。

其實就是將路由和方法存到self.routes字典中,可以通過route裝飾器的形式將路由和方法關聯,也可以通過add_route方法關聯,在app.py中使用一下。

因為url中可以存在變數,如 @app.route("/hello/{name}") ,所以在匹配時,需要進行解析,可以使用正則匹配的方式進行匹配,parse這個第三方庫已經幫我們實現了相應的正則匹配邏輯,pip安裝使用一下則可。

這里定義find_handler方法來實現對self.routes的遍歷。

了解了路由與方法關聯的原理後,就可以實現handle_request方法,該方法主要的路徑就是根據路由調度對應的方法,代碼如下。

在該方法中,首先實例化webob庫的Response對象,然後通過self.find_handler方法獲取此次請求路由對應的方法和對應的參數,比如。

它將返回hello方法對象和name參數,如果是 /hello/二兩 ,那麼name就是二兩。

因為route裝飾器可能裝飾器的類對象,比如。

此時self.find_handler方法返回的hanler就是個類,但我們希望調用的是類中的get、post、delete等方法,所以需要一個簡單的判斷邏輯,通過inspect.isclass方法判斷handler如果是類對象,那麼就通過getattr方法獲取類對象實例的中對應的請求方法。

如果類對象中沒有該方法屬性,則拋出該請求類型不被允許的錯誤,如果不是類對象或類對象中存在該方法屬性,則直接調用則可。

此外,如果方法的路由並沒有注冊到self.routes中,即404的情況,定義了defalut_response方法返回其中內容,代碼如下。

如果handle_request方法中調度的過程出現問題,則直接raise將錯誤拋出。

至此,一個最簡單的web服務就編寫完成了。

回顧Flask,Flask可以支持HTML、CSS、JavaScript等靜態文件,利用模板語言,可以構建出簡單但美觀的Web應用,我們讓TopWebF也支持這一功能,最終實現圖中的網站,完美兼容靜態文件。

Flask使用了jinja2作為其html模板引擎,ToyWebF同樣使用jinja2,jinja2其實實現一種簡單的DSL(領域內語言),讓我們可以在HTML中通過特殊的語法改變HTML的結構,該項目非常值得研究學習。

首先 pip install jinja2 ,然後就可以使用它了,在ToyWebF項目目錄中創建templates目錄,以該目錄作為默認的HTML文件根目錄,代碼如下。

首先利用jinja2的FileSystemLoader類將file system中的某個文件夾作為loader,然後初始化Environment。

在使用的過程中(即調用template方法),通過get_template方法獲得具體的某個模板並通過render方法將對應的內容傳遞給模板中的變數。

這里我們不寫前端代碼,直接去互聯網中下載模板,這里下載了Bootstrap提供的免費模板,可以自行去 https://startbootstrap.com/themes/freelancer/ 下載,下載完後,你會獲得index.html以及對應的css、jss、img等文件,將index.html移動到ToyWebF/templates中並簡單修改了一下,添加一些變數。

然後在app.py文件中為index.html定義路由以及需要的參數。

至此html文件的支持就完成了,但此時的html無法正常載入css和js,導致頁面布局非常醜陋且交互無法使用。

接著就讓ToyWebF支持css、js,首先在ToyWebF目錄下創建static文件夾用於存放css、js或img等靜態文件,隨後直接將前面下載的模板,其中的靜態文件復制到static中則可。

通過whitenoise第三方庫,可以通過簡單的幾行代碼讓web框架支持css和js,不需要依賴nginx等服務,首先 pip install whitenoise ,隨後修改API類的 __init__ 方法,代碼如下。

其實就是通過WhiteNoise將self.wsgi_app方法包裹起來,在調用API的 __call__ 方法時,直接調用self.whitenoise。

此時,如果請求web服務獲取css、js等靜態資源,WhiteNoise會獲取其內容並返回給client,它在背後會匹配靜態資源在系統中對應的文件並將其讀取返回。

至此,一開始的網頁效果就實現好了。

web服務如果出現500時,默認會返回 internal server error ,這顯得比較丑,為了讓框架使用者可以自定義500時返回的錯誤,需要添加一些代碼。

首先API初始化時,初始self.exception_handler對象並定義對應的方法添加自定義的錯誤

在handler_request方法進行請求調度時,調度的方法執行邏輯時報500,此時不再默認將錯誤拋出,而是先判斷是否有自定義錯誤處理。

在app.py中,自定義錯誤返回方法,如下。

custom_exception_handler方法只返回自定義的一段話,你完全可以替換成美觀的template。

我們可以實驗性定義一個路由來看效果。

Web服務的中間件也可以理解成鉤子,即在請求前可以對請求做一些處理或者返回Response前對Response做一下處理。

為了支持中間件,在TopWebF目錄下創建middleware.py文件,在編寫代碼前,思考一下如何實現?

回顧一下現在請求的調度邏輯。

1.通過routes裝飾器關聯路由和方法 2.通過API.whitenoise處理 3.如果是請求API介面,那麼會將參數傳遞給API.wsgi_app 4.API.wsgi_app最終會調用API.handle_request方法獲取路由對應的方法並調用該方法執行相應的邏輯

如果希望在request前以及response後做相應的操作,那麼其實就需要讓邏輯在API.handle_request前後執行,看一下代碼。

其中add方法會實例化Middleware對象,該對象會將當前的API類實例包裹起來。

Middleware.handle_request方法其實就是在self.app.handle_request前調用self.process_request方法處理request前的數據以及調用self.process_response處理response後的數據,而核心的調度邏輯,依舊交由API.handle_request方法進行處理。

這里的代碼可能會讓人感到疑惑, __call__ 方法和handle_request方法中都有self.app.handle_request(request),但其調用對象似乎不同?這個問題暫時放一下,先繼續完善代碼,然後再回來解釋。

接著在api.py中為API創建middleware屬性以及添加新中間件的方法。

隨後,在app.py中,自定義一個簡單的中間件,然後調用add_middleware方法將其添加。

定義好中間件後,在請求調度時,就需要使用中間件,為了兼容靜態文件的情況,需要對css、js、ing文件的請求路徑做一下兼容,在其路徑中加上/static前綴

緊接著,修改API的 __call__ ,兼容中間件和靜態文件,代碼如下。

至此,中間件的邏輯就完成了。

但代碼中依舊有疑惑,Middleware類中的 __call__ 方法和handle_request方法其調用的self.app到底是誰?

為了方便理解,這里一步步拆解。

如果沒有添加新的中間件,那麼請求的調度邏輯如下。

在沒有添加中間件的情況下,self.app其實就是API本身,所以 middleware.__call__ 中的self.app.handle_request就是調用API.handle_request。

如果添加了新的中間件,如上述代碼中添加了名為SimpleCustomMiddleware的中間件,此時的請求調度邏輯如下。

因為注冊中間件時,Middleware.add方法替換了原始Middleware實例中的app對象,將其替換成了SimpleCustomMiddleware,而SimpleCustomMiddleware也有app對象,SimpleCustomMiddleware中的app對象,才是API類實例。

在請求調度的過程中,就會觸發Middleware類的handle_request方法,該方法就會執行中間件相應的邏輯去處理request和response中的數據。

當然,你可以通過Middleware.add方法添加多個中間件,這就會構成棧式調用的效果,代碼如下。

啟動web服務後,其執行效果如下。