今天就跟大家聊聊有關如何理解WEB開發(fā)中的Python WSGI協(xié)議,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
Web應用程序的本質(zhì)是什么
簡單描述Web應用程序的本質(zhì),就是我們通過瀏覽器訪問互聯(lián)網(wǎng)上指定的網(wǎng)頁文件展示到瀏覽器上。
流程如下圖:
從更深層次一點的技術角度來看,由以下幾個步驟:
瀏覽器,將要請求的內(nèi)容按照HTTP協(xié)議發(fā)送服務端
服務端,根據(jù)請求內(nèi)容找到指定的HTML頁面
瀏覽器,解析請求到的HTML內(nèi)容展示出來
HTTP協(xié)議的全稱是HyperText Transfer Protocol(超文本傳輸協(xié)議)
HTTP協(xié)議是我們常用的五層協(xié)議中的應用層(5層從上到下是應用層,傳輸層,網(wǎng)絡層,數(shù)據(jù)鏈路層,物理層),HTTP協(xié)議中協(xié)定的內(nèi)容稱之為消息,消息主要包括消息頭——Header和消息體——Body。
客戶端請求時的消息稱為Request,服務端響應時的消息稱為Response.
Header:包括請求方法,HTTP版本,URI,狀態(tài)碼,COOKIE等
Body:是響應或者請求時的內(nèi)容,包含HTML,CSS,JS等
HTTP協(xié)議這里就不做過多的描述,可以到點擊這里深入了解HTTP協(xié)議
HTML的全稱是Hyper Text Markup Language(超文本標記語言)
簡單點說,HTML 是一種由不同元素組成的標記語言,它定義了網(wǎng)頁內(nèi)容的含義和結構,所有我們在瀏覽器中看到的內(nèi)容都是由一個一個的元素組成。除 HTML 以外的其它技術則通常用來描述一個網(wǎng)頁的表現(xiàn)與展示效果(如 CSS),或功能與行為(如 JavaScript)。
WEB開發(fā)的歷程
靜態(tài)開發(fā)
直接將寫好的HTML頁面放在服務器上,然后直接通過瀏覽器訪問指定服務器的文件。
動態(tài)開發(fā)
隨著我們的需求變化單獨使用靜態(tài)開發(fā)已經(jīng)不能完全滿足我們。 例如我們查看的頁面只有部分內(nèi)容會變化,那我們再去開發(fā)相同的頁面。 一是開發(fā)上是一種重復工作,完全是一種浪費。 二是數(shù)據(jù)量變化巨大時,完全是跟不上速度,并且數(shù)據(jù)變化也不是定時更新。 為了應對這種問題,動態(tài)網(wǎng)頁技術也就誕生了。早期的動態(tài)網(wǎng)頁開發(fā)技術是CGI CGI全稱:Common Gateway Interface,通用網(wǎng)關接口,它是一段程序,運行在服務器上如:HTTP 服務器, 提供同客戶端 HTML 頁面的接口。 CGI 程序可以是 Python 腳本,PERL 腳本,SHELL 腳本,C 或者 C++ 程序等。 各種編程語言也針對動態(tài)網(wǎng)頁開發(fā)給出不同的解決方案,JAVA的servlet,Python的WSGI協(xié)議等。
Python的WSGI協(xié)議也是我們本章要講的內(nèi)容
CGI流程
WSGI的流程
WSGI全稱是Web Server Gateway Interface,其主要作用是Web服務器與Python Web應用程序或框架之間的建議標準接口,以促進跨各種Web服務器的Web應用程序可移植性。
WSGI并不是框架而只是一種協(xié)議,我們可以將WSGI協(xié)議分成三個組件Application,Server,Middleware和協(xié)議中傳輸?shù)膬?nèi)容。
將這三個組件對映射到我們具體使用的組件是:
Server:常用的有uWSGI,gunicorn等
Application:Django,F(xiàn)lask等
Middleware: Flask等框架中的裝飾器
點擊這里查看官方關于WSGI協(xié)議的定義
組件Application
應用程序,是一個可重復調(diào)用的可調(diào)用對象,在Python中可以是一個函數(shù),也可以是一個類,如果是類的話要實現(xiàn)__call__方法,要求這個可調(diào)用對象接收2個參數(shù),返回一個內(nèi)容結果
接收的2個參數(shù)分別是environ和start_response。
environ是web服務器解析HTTP協(xié)議的一些信息,例如請求方法,請求URI等信息構成的一個Dict對象。
start_response是一個函數(shù),接收2個參數(shù),一個是HTTP狀態(tài)碼,一個HTTP消息中的響應頭。
依照官方提供的示例用函數(shù)實現(xiàn)應用程序
def simple_app(environ, start_response): """Simplest possible application object""" status = '200 OK' response_headers = [('Content-type', 'text/plain; charset=utf-8')] start_response(status, response_headers) return_body = [] for key, value in environ.items(): return_body.append("{} : {}".format(key, value)) return_body.append("\nHello WSGI!") # 返回結果必須是bytes return ["\n".join(return_body).encode("utf-8")]
組件Server
Web服務器,主要是實現(xiàn)相應的信息轉(zhuǎn)換,將網(wǎng)絡請求中的信息,按照HTTP協(xié)議將內(nèi)容拿出,同時按照WSGI協(xié)議組裝成新的數(shù)據(jù),同時將提供的start_response傳遞給Application。最后接收Application返回的內(nèi)容,按照WSGI協(xié)議解析出。最終按照HTTP協(xié)議組織好內(nèi)容返回就完成了一次請求。
Server操作的步驟如下:
根據(jù)HTTP協(xié)議內(nèi)容構建envrion
提供一個start_response函數(shù),接收HTTP STATU 和 HTTP HEADER
將envrion和start_response作為參數(shù)調(diào)用Application
接收Application返回的結果
按照HTTP協(xié)議,順序?qū)懭際TTP響應頭(start_response接收),HTTP響應體(Application返回結果)
下面這個是pep3333協(xié)議中的一個server例子,按照CGI請求的方式來實現(xiàn)。
import os, sys enc, esc = sys.getfilesystemencoding(), 'surrogateescape' def unicode_to_wsgi(u): # Convert an environment variable to a WSGI "bytes-as-unicode" string return u.encode(enc, esc).decode('iso-8859-1') def wsgi_to_bytes(s): return s.encode('iso-8859-1') def run_with_cgi(application): # 按照WSGI協(xié)議,構建environ內(nèi)容 # 1類 CGI相關的變量,此腳本就是用于cgi執(zhí)行,所以前面的web服務器已經(jīng)將CGI變量封裝好,這里直接使用 environ = {k: unicode_to_wsgi(v) for k,v in os.environ.items()} # 2類 wsgi定義的變量 environ['wsgi.input'] = sys.stdin.buffer environ['wsgi.errors'] = sys.stderr environ['wsgi.version'] = (1, 0) environ['wsgi.multithread'] = False environ['wsgi.multiprocess'] = True environ['wsgi.run_once'] = True if environ.get('HTTPS', 'off') in ('on', '1'): environ['wsgi.url_scheme'] = 'https' else: environ['wsgi.url_scheme'] = 'http' headers_set = [] headers_sent = [] def write(data): # 將內(nèi)容返回 out = sys.stdout.buffer if not headers_set: raise AssertionError("write() before start_response()") elif not headers_sent: # Before the first output, send the stored headers status, response_headers = headers_sent[:] = headers_set out.write(wsgi_to_bytes('Status: %s\r\n' % status)) for header in response_headers: out.write(wsgi_to_bytes('%s: %s\r\n' % header)) out.write(wsgi_to_bytes('\r\n')) out.write(data) out.flush() def start_response(status, response_headers, exc_info=None): if exc_info: try: if headers_sent: # Re-raise original exception if headers sent raise exc_info[1].with_traceback(exc_info[2]) finally: exc_info = None # avoid dangling circular ref elif headers_set: raise AssertionError("Headers already set!") headers_set[:] = [status, response_headers] # Note: error checking on the headers should happen here, # *after* the headers are set. That way, if an error # occurs, start_response can only be re-called with # exc_info set. return write # 將上面處理的參數(shù)交給應用程序 result = application(environ, start_response) try: # 將請求到的結果寫回。 for data in result: if data: # don't send headers until body appears write(data) if not headers_sent: write('') # send headers now if body was empty finally: if hasattr(result, 'close'): result.close()
組件Middleware
中間件,可以理解為對應用程序的一組裝飾器。
在應用程序端看來,它可以提供一個類start_response函數(shù),可以想start_response函數(shù)一樣接收HTTP STATU和Headers;和environ。
在服務端看來,他可以接收2個參數(shù),并且可以返回一個類Application對象。
下面看一個例子,記錄每次請求的消耗時間:
import time class ResponseTimingMiddleware(object): """記錄請求耗時""" def __init__(self, app): self.app = app def __call__(self, environ, start_response): start_time = time.time() response = self.app(environ, start_response) response_time = (time.time() - start_time) * 1000 timing_text = "記錄請求耗時中間件輸出\n\n本次請求耗時: {:.10f}ms \n\n\n".format(response_time) response.append(timing_text.encode('utf-8')) return response
協(xié)議內(nèi)容
重點看environ有哪些內(nèi)容,這里面才是瀏覽器每次請求時的信息。再深入一點探索,就是HTTP請求消息中的請求頭和請求體都是怎么定義及怎么回去的。
environ是一個字典,environ中要包含CGI定義的變量,主要是將HTTP協(xié)議中的內(nèi)容,比如請求方法,POST/GET,請求URI等,另外是WSGI協(xié)議自己定義的變量,比如請求body中要讀取的信息等。列一下主要變量項如下:
CGI相關變量
變量說明REQUEST_METHODPOST,GET等,HTTP請求的動詞標識SERVER_PROTOCOL服務器運行的HTTP協(xié)議. 這里當是HTTP/1.0.PATH_INFO附加的路徑信息, 由瀏覽器發(fā)出.QUERY_STRING請求URL的“?”后面的部分CONTENT_TYPEHTTP請求中任何Content-Type字段的內(nèi)容CONTENT_LENGTH標準輸入口的字節(jié)數(shù).HTTP_[變量]其他一些變量,例如HTTP_ACCEPT,HTTP_REFERER等
上述內(nèi)容是動態(tài)開發(fā)的根基,只有根據(jù)上述內(nèi)容才可以標準化的動態(tài)處理請求。
WSGI定義變量
變量說明wsgi.versionWSGI版本,要求是元組(1,0),標識WSGI 1.0協(xié)議wsgi.url_scheme表示調(diào)用應用程序的URL的協(xié)議,http或httpswsgi.input類文件對象,讀取HTTP請求體字節(jié)的輸入流wsgi.errors類文件對象,寫入錯誤輸出的輸出流wsgi.multithread如果是多線程,則設置為True,否則為False。wsgi.multiprocess如果是多進程,則設置為True,否則為False。wsgi.run_once如果只需要運行一次,設置為True
WSGI協(xié)議對于兩個輸入輸出流有一些方法必須要實現(xiàn)
流方法wsgi.inputread(size)wsgi.inputreadline()wsgi.inputreadlines(hint)wsgi.inputiter()wsgi.errorsflush()wsgi.errorswrite(str)wsgi.errorswritelines(seq)
這些基本上就是WSGI協(xié)議中定義的主要變量,也基本上涵蓋了我們開發(fā)時所需要的變量。
Server端按照協(xié)議的內(nèi)容生成這些environ字典,然后將請求信息交給Application,Application根據(jù)這些信息確認請求要處理的內(nèi)容,然后返回響應消息。從頭順下來就是這個流程。
示例展示
Server端涉及到實現(xiàn)http相關內(nèi)容,我們直接使用python內(nèi)置wsgiref來實現(xiàn),具體代碼如下:
import time from wsgiref.simple_server import make_server class ResponseTimingMiddleware(object): """記錄請求耗時""" def __init__(self, app): self.app = app def __call__(self, environ, start_response): start_time = time.time() response = self.app(environ, start_response) response_time = (time.time() - start_time) * 1000 timing_text = "記錄請求耗時中間件輸出\n\n本次請求耗時: {:.10f}ms \n\n\n".format(response_time) response.append(timing_text.encode('utf-8')) return response def simple_app(environ, start_response): """Simplest possible application object""" status = '200 OK' response_headers = [('Content-type', 'text/plain; charset=utf-8')] start_response(status, response_headers) return_body = [] for key, value in environ.items(): return_body.append("{} : {}".format(key, value)) return_body.append("\nHello WSGI!") # 返回結果必須是bytes return ["\n".join(return_body).encode("utf-8")] # 創(chuàng)建應用程序 app = ResponseTimingMiddleware(simple_app) # 啟動服務,監(jiān)聽8080 httpd = make_server('localhost', 8080, app) httpd.serve_forever()
啟動服務后,我們打開瀏覽器訪問http://localhost:8080,執(zhí)行結果如下。
上圖可以看到我們前面提到的中間件以及Application中執(zhí)行返回的結果全都實現(xiàn)。
看完上述內(nèi)容,你們對如何理解WEB開發(fā)中的Python WSGI協(xié)議有進一步的了解嗎?如果還想了解更多知識或者相關內(nèi)容,請關注創(chuàng)新互聯(lián)-成都網(wǎng)站建設公司行業(yè)資訊頻道,感謝大家的支持。
本文名稱:如何理解WEB開發(fā)中的PythonWSGI協(xié)議-創(chuàng)新互聯(lián)
分享鏈接:http://www.rwnh.cn/article44/hohee.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、網(wǎng)站內(nèi)鏈、動態(tài)網(wǎng)站、品牌網(wǎng)站建設、軟件開發(fā)、關鍵詞優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)