中間件
一、剖析:
在前面講session部分提到過(guò):請(qǐng)求一進(jìn)來(lái),F(xiàn)lask會(huì)自動(dòng)調(diào)用應(yīng)用程序?qū)ο蟆綟lask(__name__)】的__call__
方法,這個(gè)方法負(fù)責(zé)處理請(qǐng)求并返回響應(yīng)(其實(shí)如下圖:其內(nèi)部就是wsgi_app方法
)。它是WSGI規(guī)范所要求的。
在wsgi_app
方法內(nèi)部,F(xiàn)lask會(huì)根據(jù)路由規(guī)則和視圖函數(shù)來(lái)確定如何處理請(qǐng)求,并生成相應(yīng)的響應(yīng)。最終,wsgi_app
方法會(huì)將響應(yīng)返回給Web服務(wù)器,供其發(fā)送給客戶端(前面詳細(xì)講過(guò),后面還會(huì)再細(xì)細(xì)剖析)。
所以,這里就有一個(gè)坑可以讓我們操作,即我們可以通過(guò)覆寫wsgi_app
方法,實(shí)現(xiàn)自己的中間件邏輯,例如身份驗(yàn)證、日志記錄等。
而且這個(gè)坑牛逼之處在于:我們可以借由它實(shí)現(xiàn)在最開始的開始和最后的最后做操作?。?!多加理解,底下會(huì)詳細(xì)講解。
結(jié)合實(shí)戰(zhàn)講解—通過(guò)覆寫wsgi_app
函數(shù)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的中間件:
from flask import Flask
app = Flask(__name__)
class MyMiddleware:
def __init__(self, old_wsgi_app):
# 服務(wù)端啟動(dòng)時(shí),自動(dòng)執(zhí)行
self.old_wsgi_app = old_wsgi_app
def __call__(self, environ, start_response):
# 每次有用戶請(qǐng)求到來(lái)時(shí)執(zhí)行
# 在請(qǐng)求到達(dá)視圖函數(shù)之前執(zhí)行的代碼
print("Before request")
# 調(diào)用原始的wsgi_app函數(shù)處理請(qǐng)求
response = self.old_wsgi_app(environ, start_response)
# 在響應(yīng)發(fā)送給客戶端之前執(zhí)行的代碼
print("After request")
return response
@app.route('/')
def home():
return "Hello, GuHanZhe~"
# 設(shè)置自定義中間件實(shí)例為應(yīng)用程序?qū)ο蟮膚sgi_app屬性
app.wsgi_app = MyMiddleware(app.wsgi_app)
if __name__ == '__main__':
app.run()
在上述示例中,我定義了一個(gè)名為MyMiddleware
的類,它接受一個(gè)Flask應(yīng)用程序?qū)ο蟮?code>wsgi_app方法作為參數(shù)。該類實(shí)現(xiàn)了__call__
方法,這是一個(gè)wsgi應(yīng)用程序必須具備的方法。在__call__
方法中,大家可以編寫自己的中間件邏輯。
在__call__
方法中,大家可以首先執(zhí)行在請(qǐng)求到達(dá)視圖函數(shù)之前需要執(zhí)行的代碼,然后調(diào)用原始的wsgi_app
方法處理請(qǐng)求,并將響應(yīng)保存在response
變量中。最后,在響應(yīng)發(fā)送給客戶端之前,大家可以執(zhí)行一些在響應(yīng)階段需要執(zhí)行的代碼。
通過(guò)創(chuàng)建自定義中間件實(shí)例,并將其設(shè)置為Flask應(yīng)用程序?qū)ο蟮?code>wsgi_app屬性,就可以使用自定義的中間件了。
請(qǐng)注意,自定義中間件類必須實(shí)現(xiàn)__call__
方法,并且接受environ
和start_response
參數(shù),這是遵守WSGI規(guī)范。
而且看Flask的wsgi_app函數(shù)
源碼,也是如此:
需要注意的是:在print("Before request")
部分做操作時(shí),只有原生的請(qǐng)求相關(guān)的數(shù)據(jù)environ,所以就只能對(duì)原生的environ做操作! request和session是都沒(méi)有的!!!
二、應(yīng)用點(diǎn):
通過(guò)覆寫wsgi_app
函數(shù)實(shí)現(xiàn)的自定義中間件可以有如下幾種在開發(fā)時(shí)的應(yīng)用點(diǎn):
-
可以對(duì)請(qǐng)求進(jìn)行預(yù)處理:通過(guò)覆寫
wsgi_app
函數(shù),可以在請(qǐng)求到達(dá)視圖函數(shù)之前執(zhí)行一些代碼邏輯,例如身份驗(yàn)證、參數(shù)解析、請(qǐng)求日志記錄等。這樣可以方便地對(duì)請(qǐng)求進(jìn)行預(yù)處理,并根據(jù)需要做出相應(yīng)的處理。 -
可以對(duì)響應(yīng)進(jìn)行后處理:同樣地,在覆寫
wsgi_app
函數(shù)時(shí),還可以在響應(yīng)發(fā)送給客戶端之前執(zhí)行一些代碼邏輯,例如響應(yīng)的加工、錯(cuò)誤處理、響應(yīng)日志記錄等。這樣可以方便地對(duì)響應(yīng)進(jìn)行后處理,以滿足特定的需求。 -
可以實(shí)現(xiàn)自定義中間件功能:通過(guò)覆寫
wsgi_app
函數(shù),可以實(shí)現(xiàn)自定義的中間件功能。中間件是一種可重用的組件,可以用于添加額外的邏輯或修改請(qǐng)求/響應(yīng)的行為。可以根據(jù)具體需求編寫自己的中間件,并將其插入Flask應(yīng)用程序?qū)ο蟮奶幚砹鞒讨小?/p> -
可以實(shí)現(xiàn)多個(gè)中間件的串聯(lián):Flask允許使用多個(gè)中間件,并且這些中間件可以按照特定的順序串聯(lián)起來(lái)。通過(guò)覆寫
wsgi_app
函數(shù),可以輕松地將多個(gè)中間件組合起來(lái),形成一個(gè)中間件鏈條。每個(gè)中間件都可以獨(dú)立地處理請(qǐng)求和響應(yīng),并將處理結(jié)果傳遞給下一個(gè)中間件。 -
可以修改請(qǐng)求和響應(yīng):通過(guò)覆寫
wsgi_app
函數(shù),可以自由地修改請(qǐng)求和響應(yīng)對(duì)象。這包括添加、刪除或修改請(qǐng)求頭部信息,修改請(qǐng)求體內(nèi)容,修改響應(yīng)狀態(tài)碼,添加響應(yīng)頭部信息等。這樣可以實(shí)現(xiàn)更加靈活和定制化的請(qǐng)求/響應(yīng)處理。
總而言之,覆寫wsgi_app
函數(shù)實(shí)現(xiàn)中間件提供了對(duì)請(qǐng)求和響應(yīng)進(jìn)行預(yù)處理和后處理的能力,同時(shí)也允許編寫自定義的中間件功能。這樣可以增強(qiáng)Flask應(yīng)用程序的功能和靈活性,滿足特定的需求。
就比如要做IP黑名單,就可以在before里直接寫邏輯(environ里有請(qǐng)求IP信息),這樣就可以在最開始的開始直接限制!
拓展:
(1)在Python中,當(dāng)一個(gè)對(duì)象后面能加括號(hào),那么這個(gè)對(duì)象可能是什么?
-
函數(shù):一個(gè)函數(shù)是可調(diào)用的對(duì)象。通過(guò)在函數(shù)名后加上括號(hào),可以執(zhí)行該函數(shù)并傳遞相應(yīng)的參數(shù)。
-
方法:方法是屬于類的函數(shù)。通過(guò)在實(shí)例或類名后加上括號(hào),可以調(diào)用該方法并傳遞相應(yīng)的參數(shù)。
-
類:類本身也是可調(diào)用的對(duì)象。通過(guò)在類名后加上括號(hào),可以創(chuàng)建類的實(shí)例。
-
對(duì)象:某個(gè)類的實(shí)例對(duì)象也可以是可調(diào)用的對(duì)象。通過(guò)在對(duì)象名后加上括號(hào),可以調(diào)用該對(duì)象所屬類中定義的特殊方法,例如
__call__()
方法。
(2)在Python中,函數(shù)和方法分別是什么?
首先,要認(rèn)識(shí)到在Python中,函數(shù)(function)和方法(method)是兩種不同的概念。這也是為啥我給的問(wèn)題是“函數(shù)和方法分別是什么?”
-
函數(shù)(function)是一段封裝了特定功能的可重用代碼塊。它接收輸入?yún)?shù),執(zhí)行特定的操作,并返回結(jié)果。 函數(shù)可以在任何地方定義和使用,不依賴于任何類或?qū)ο?。它們通常用于模塊化代碼、提高代碼的復(fù)用性和可維護(hù)性。
例如,下面是一個(gè)簡(jiǎn)單的函數(shù)示例:
def add(a, b):
return a + b
result = add(2, 3)
print(result)
-
**方法(method)是屬于某個(gè)類的函數(shù)。**它定義在類的內(nèi)部,并且可以訪問(wèn)類的屬性和其他方法。**方法通過(guò)對(duì)類的實(shí)例進(jìn)行調(diào)用來(lái)執(zhí)行相應(yīng)的操作。**每個(gè)方法的第一個(gè)參數(shù)通常都是
self
,它表示方法所屬的實(shí)例對(duì)象。例如,下面是一個(gè)簡(jiǎn)單的類和方法示例:
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
circle = Circle(5)
circle_area = circle.area()
print(circle_area) # 輸出:78.5
在上面的示例中,area()
是 Circle
類的一個(gè)方法,它可以通過(guò) circle.area()
的方式進(jìn)行調(diào)用。
需要注意的是,對(duì)于一個(gè)類里面的函數(shù),它究竟真是函數(shù)還是方法,取決于誰(shuí)調(diào)用它?。?!
舉個(gè)例子:
上圖中Test.index執(zhí)行的話,此時(shí)index作為函數(shù),意思是執(zhí)行類里面的函數(shù);
而下面是通過(guò)類的實(shí)例調(diào)用,所以此時(shí)是方法。
或者這樣也能證明:
from types import MethodType, FunctionType
class Test(object):
def index(self):
pass
# print(Test.index)
print(isinstance(Test.index, FunctionType))
test = Test()
# print(test.index)
print(isinstance(test.index, MethodType))
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-754371.html
總結(jié):函數(shù)是獨(dú)立的可調(diào)用代碼塊,而方法是屬于類的函數(shù),需要通過(guò)類的實(shí)例進(jìn)行調(diào)用。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-754371.html
到了這里,關(guān)于(十五)Flask覆寫wsgi_app函數(shù)實(shí)現(xiàn)自定義中間件的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!