WSGI(Web 服務(wù)器網(wǎng)關(guān)接口)是python中所定義的Web Server和Web APP之間或框架之間的接口標(biāo)準(zhǔn)規(guī)范。當(dāng)使用 Python 進(jìn)行 web 開發(fā)時(shí),要深刻理解 Django、Flask、Tornado等 web 框架,WSGI是你繞不過去的檻兒。WSGI接口規(guī)范的目的就是規(guī)范Web服務(wù)器與Web應(yīng)用之間的交互,在協(xié)議之間進(jìn)行轉(zhuǎn)換。- Web服務(wù)器(Server): 監(jiān)聽某個(gè)端口的http服務(wù)器
- Web應(yīng)用程序(APP): 指的是可以被調(diào)用的一個(gè)對象,一般指的是一個(gè)函數(shù) 或者 包含一個(gè)
__call__ 方法的類的實(shí)例 - Web中間件(Middleware):處于服務(wù)器和應(yīng)用中間,起到承接的作用,用Python的術(shù)語來說,中間件就類似于一個(gè)裝飾器
#這是一個(gè)appdef app(environ, start_response): return [] def middleware(environ, start_response): #這里編寫中間件的代碼 return app(environ, start_response) django 整個(gè)項(xiàng)目實(shí)際上也是這三部分組成,我們在執(zhí)行python manage.py runserver 的時(shí)候,
- 就首先啟動了一個(gè)8000端口的http服務(wù)器(這個(gè)就是Web服務(wù)器)
- 然后 django里面的中間件(這個(gè)就是上述所說的Web中間件),這個(gè)是定義在
settings.py 文件的 MIDDLEWARE - 最后 django加載里面的app(這個(gè)就是上述所說的Web應(yīng)用程序),這個(gè)是定義在
settings.py 文件的 INSTALLED_APPS
WSGI App介紹WSGI APP是一個(gè)可調(diào)用的對象(callable object),常用的可調(diào)用的對象有三種:def app(environ, start_response): 這是一個(gè)app return [] 2.一個(gè)實(shí)現(xiàn)__call__() 方法的類的實(shí)例:class app:
def __call__(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [] 這個(gè)可調(diào)用的對象有幾點(diǎn)需要說明一下:- 接收兩個(gè)參數(shù)
environ 和start_response : environ 是一個(gè)字典,里面儲存了HTTP request的所有內(nèi)容。在django里面,通常會把environ 封裝成為一個(gè)request。start_response 是一個(gè)WSGI Server(http 服務(wù)器)傳遞過來的函數(shù),用于將response header, status傳遞給Server。start_response(status, headers) , 它的作用是返回狀態(tài)碼 以及 頭部信息, status必須是一個(gè)字符串,格式是 “狀態(tài)碼 + 說明”。- headers 是一個(gè)數(shù)組,按照 [(key, value), (key, value) ] 這樣的格式來組織。
- 它需要返回一個(gè)可迭代的值,用于將response body傳遞給Server。
["hello world", "baby"]
WSGI Server介紹WSGI Server可以理解為就是一個(gè)實(shí)現(xiàn)了wsgi協(xié)議的http服務(wù)器,使用wsgi協(xié)議的方式來調(diào)用WSGI APP。1.http 服務(wù)器:這里具體的代碼就不寫了,大概就是socket = eventlet.listen(('localhost', '8000'), backlog = 10) 定義一個(gè)wsgi http服務(wù)器server = eventlet.spawn(event.wsgi.server, socket, app) 把service 和 app進(jìn)行綁定 def run(application): #服務(wù)器程序調(diào)用應(yīng)用程序 environ = {}#設(shè)定參數(shù) def start_response(status, headers): #設(shè)定狀態(tài)和頭部參數(shù)的回調(diào)函數(shù) pass
result = application(environ, start_response)#調(diào)用APP的__call__函數(shù)(這里APP是一個(gè)類) def write(data): # 這是把響應(yīng)發(fā)到前端的函數(shù) pass def data in result: # 迭代訪問,把響應(yīng)發(fā)到前端 write(data)
- 設(shè)定app所需要的參數(shù)(environ,start_response)
- 迭代訪問app的返回結(jié)果(response body),并傳給客戶端
但實(shí)際上已經(jīng)有很多已經(jīng)封裝好的WSGI Server供我們使用,只需簡單的將APP與一些其他的參數(shù)綁定來創(chuàng)建一個(gè)Server,而這個(gè)Server會將它接收到的request傳遞給綁定的APP。from wsgiref.simple_server import make_server
# 定義我們一個(gè)最簡單的app def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [b'<h1>Hello, web!</h1>']
# 創(chuàng)建一個(gè)服務(wù)器,IP地址為空,端口是8000,處理函數(shù)是application: httpd = make_server('', 8000, application) print('Serving HTTP on port 8000...') # 開始監(jiān)聽HTTP請求: httpd.serve_forever() 這里我們已經(jīng)自己編寫了一個(gè)最基礎(chǔ)的web框架,是不是很激動,django的本質(zhì)就是這樣的一種形式,是不是感覺發(fā)現(xiàn)了新大陸。
WSGI Middleware介紹middleware 的概念沒有appllication 和server 那么容易理解。假設(shè)一個(gè)符合application 標(biāo)準(zhǔn)的可調(diào)用對象,它接受可調(diào)用對象作為參數(shù),返回一個(gè)可調(diào)用對象的對象。那么對于server 來說,它是一個(gè)符合標(biāo)準(zhǔn)的可調(diào)用對象,因此是application 。而對于application 來說,它可以調(diào)用application ,因此是server 。這樣的可調(diào)用對象稱為middleware 。middleware 的概念非常接近decorator 。中間件對于app來說,它是一個(gè)service. 但是對于service來說,它確實(shí)一個(gè)app。文字說的不清晰,還是用代碼來說比較好。# 這是一個(gè)標(biāo)準(zhǔn)的application object def index(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return ['index page']
# 這是一個(gè)標(biāo)準(zhǔn)的application object def hello(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return ['hello page']
# 這是一個(gè)標(biāo)準(zhǔn)的application object def not_found(environ, start_response): start_response('404 NOT FOUND', [('Content-Type', 'text/plain')]) return ['Not Found Page']
###上面我們定義了三個(gè)app ### 然后我們定義一個(gè)中間件 middleware,這個(gè)中間件的形式是跟app是一樣的 def application(environ, start_response): path = environ.get('PATH_INFO', '').lstrip('/') #這句代碼是獲取url
urls = [ # 這里定義路由 ('index', index), ('hello', hello) ]
for item in urls: # 這里根據(jù)路由,執(zhí)行不同的app if item[0] == path: app = item[1] return app(environ, start_response) else: return not_found(environ, start_response) # 如果找不到,則執(zhí)行默認(rèn)的app from wsgiref.simple_server import make_server
# 創(chuàng)建一個(gè)服務(wù)器,IP地址為空,端口是8000,處理函數(shù)是application: httpd = make_server('', 8000, application) httpd.serve_forever() 看到?jīng)]有,這個(gè)例子比上面的更加完善,利用中間件實(shí)現(xiàn)了路由的功能,把django最基礎(chǔ)的功能完整的展示出來 中間件除了路由之外,還可以做很多事情,最常見的還有:- 負(fù)載均衡,轉(zhuǎn)發(fā)用戶請求
- 預(yù)處理 XSL 等相關(guān)數(shù)據(jù)
|