'Python學習教程:WEB開發——Python WSGI協議詳解'

"

Web應用程序開發

Web應用程序的本質是什麼

簡單描述Web應用程序的本質,就是我們通過瀏覽器訪問互聯網上指定的網頁文件展示到瀏覽器上。

流程如下圖:

"

Web應用程序開發

Web應用程序的本質是什麼

簡單描述Web應用程序的本質,就是我們通過瀏覽器訪問互聯網上指定的網頁文件展示到瀏覽器上。

流程如下圖:

Python學習教程:WEB開發——Python WSGI協議詳解

從更深層次一點的技術角度來看,由以下幾個步驟:

  • 瀏覽器,將要請求的內容按照HTTP協議發送服務端
  • 服務端,根據請求內容找到指定的HTML頁面
  • 瀏覽器,解析請求到的HTML內容展示出來

HTTP協議的全稱是HyperText Transfer Protocol(超文本傳輸協議)

HTTP協議是我們常用的五層協議中的應用層(5層從上到下是應用層,傳輸層,網絡層,數據鏈路層,物理層),HTTP協議中協定的內容稱之為消息,消息主要包括消息頭——Header和消息體——Body。

客戶端請求時的消息稱為Request,服務端響應時的消息稱為Response.

Header:包括請求方法,HTTP版本,URI,狀態碼,COOKIE等

Body:是響應或者請求時的內容,包含HTML,CSS,JS等

HTTP協議這裡就不做過多的描述,可以到點擊這裡深入瞭解HTTP協議

HTML的全稱是Hyper Text Markup Language(超文本標記語言)

簡單點說,HTML 是一種由不同元素組成的標記語言,它定義了網頁內容的含義和結構,所有我們在瀏覽器中看到的內容都是由一個一個的元素組成。除 HTML 以外的其它技術則通常用來描述一個網頁的表現與展示效果(如 CSS),或功能與行為(如 JavaScript)。

WEB開發的歷程

靜態開發

直接將寫好的HTML頁面放在服務器上,然後直接通過瀏覽器訪問指定服務器的文件。

動態開發

隨著我們的需求變化單獨使用靜態開發已經不能完全滿足我們。
例如我們查看的頁面只有部分內容會變化,那我們再去開發相同的頁面。
一是開發上是一種重複工作,完全是一種浪費。
二是數據量變化巨大時,完全是跟不上速度,並且數據變化也不是定時更新。
為了應對這種問題,動態網頁技術也就誕生了。早期的動態網頁開發技術是CGI
CGI全稱:Common Gateway Interface,通用網關接口,它是一段程序,運行在服務器上如:HTTP 服務器,
提供同客戶端 HTML 頁面的接口。
CGI 程序可以是 Python 腳本,PERL 腳本,SHELL 腳本,C 或者 C++ 程序等。
各種編程語言也針對動態網頁開發給出不同的解決方案,JAVA的servlet,Python的WSGI協議等。

Python的WSGI協議也是我們本章要講的內容

CGI流程

"

Web應用程序開發

Web應用程序的本質是什麼

簡單描述Web應用程序的本質,就是我們通過瀏覽器訪問互聯網上指定的網頁文件展示到瀏覽器上。

流程如下圖:

Python學習教程:WEB開發——Python WSGI協議詳解

從更深層次一點的技術角度來看,由以下幾個步驟:

  • 瀏覽器,將要請求的內容按照HTTP協議發送服務端
  • 服務端,根據請求內容找到指定的HTML頁面
  • 瀏覽器,解析請求到的HTML內容展示出來

HTTP協議的全稱是HyperText Transfer Protocol(超文本傳輸協議)

HTTP協議是我們常用的五層協議中的應用層(5層從上到下是應用層,傳輸層,網絡層,數據鏈路層,物理層),HTTP協議中協定的內容稱之為消息,消息主要包括消息頭——Header和消息體——Body。

客戶端請求時的消息稱為Request,服務端響應時的消息稱為Response.

Header:包括請求方法,HTTP版本,URI,狀態碼,COOKIE等

Body:是響應或者請求時的內容,包含HTML,CSS,JS等

HTTP協議這裡就不做過多的描述,可以到點擊這裡深入瞭解HTTP協議

HTML的全稱是Hyper Text Markup Language(超文本標記語言)

簡單點說,HTML 是一種由不同元素組成的標記語言,它定義了網頁內容的含義和結構,所有我們在瀏覽器中看到的內容都是由一個一個的元素組成。除 HTML 以外的其它技術則通常用來描述一個網頁的表現與展示效果(如 CSS),或功能與行為(如 JavaScript)。

WEB開發的歷程

靜態開發

直接將寫好的HTML頁面放在服務器上,然後直接通過瀏覽器訪問指定服務器的文件。

動態開發

隨著我們的需求變化單獨使用靜態開發已經不能完全滿足我們。
例如我們查看的頁面只有部分內容會變化,那我們再去開發相同的頁面。
一是開發上是一種重複工作,完全是一種浪費。
二是數據量變化巨大時,完全是跟不上速度,並且數據變化也不是定時更新。
為了應對這種問題,動態網頁技術也就誕生了。早期的動態網頁開發技術是CGI
CGI全稱:Common Gateway Interface,通用網關接口,它是一段程序,運行在服務器上如:HTTP 服務器,
提供同客戶端 HTML 頁面的接口。
CGI 程序可以是 Python 腳本,PERL 腳本,SHELL 腳本,C 或者 C++ 程序等。
各種編程語言也針對動態網頁開發給出不同的解決方案,JAVA的servlet,Python的WSGI協議等。

Python的WSGI協議也是我們本章要講的內容

CGI流程

Python學習教程:WEB開發——Python WSGI協議詳解

WSGI的流程

"

Web應用程序開發

Web應用程序的本質是什麼

簡單描述Web應用程序的本質,就是我們通過瀏覽器訪問互聯網上指定的網頁文件展示到瀏覽器上。

流程如下圖:

Python學習教程:WEB開發——Python WSGI協議詳解

從更深層次一點的技術角度來看,由以下幾個步驟:

  • 瀏覽器,將要請求的內容按照HTTP協議發送服務端
  • 服務端,根據請求內容找到指定的HTML頁面
  • 瀏覽器,解析請求到的HTML內容展示出來

HTTP協議的全稱是HyperText Transfer Protocol(超文本傳輸協議)

HTTP協議是我們常用的五層協議中的應用層(5層從上到下是應用層,傳輸層,網絡層,數據鏈路層,物理層),HTTP協議中協定的內容稱之為消息,消息主要包括消息頭——Header和消息體——Body。

客戶端請求時的消息稱為Request,服務端響應時的消息稱為Response.

Header:包括請求方法,HTTP版本,URI,狀態碼,COOKIE等

Body:是響應或者請求時的內容,包含HTML,CSS,JS等

HTTP協議這裡就不做過多的描述,可以到點擊這裡深入瞭解HTTP協議

HTML的全稱是Hyper Text Markup Language(超文本標記語言)

簡單點說,HTML 是一種由不同元素組成的標記語言,它定義了網頁內容的含義和結構,所有我們在瀏覽器中看到的內容都是由一個一個的元素組成。除 HTML 以外的其它技術則通常用來描述一個網頁的表現與展示效果(如 CSS),或功能與行為(如 JavaScript)。

WEB開發的歷程

靜態開發

直接將寫好的HTML頁面放在服務器上,然後直接通過瀏覽器訪問指定服務器的文件。

動態開發

隨著我們的需求變化單獨使用靜態開發已經不能完全滿足我們。
例如我們查看的頁面只有部分內容會變化,那我們再去開發相同的頁面。
一是開發上是一種重複工作,完全是一種浪費。
二是數據量變化巨大時,完全是跟不上速度,並且數據變化也不是定時更新。
為了應對這種問題,動態網頁技術也就誕生了。早期的動態網頁開發技術是CGI
CGI全稱:Common Gateway Interface,通用網關接口,它是一段程序,運行在服務器上如:HTTP 服務器,
提供同客戶端 HTML 頁面的接口。
CGI 程序可以是 Python 腳本,PERL 腳本,SHELL 腳本,C 或者 C++ 程序等。
各種編程語言也針對動態網頁開發給出不同的解決方案,JAVA的servlet,Python的WSGI協議等。

Python的WSGI協議也是我們本章要講的內容

CGI流程

Python學習教程:WEB開發——Python WSGI協議詳解

WSGI的流程

Python學習教程:WEB開發——Python WSGI協議詳解

什麼是WSGI

WSGI全稱是Web Server Gateway Interface,其主要作用是Web服務器與Python Web應用程序或框架之間的建議標準接口,以促進跨各種Web服務器的Web應用程序可移植性。

WSGI並不是框架而只是一種協議,我們可以將WSGI協議分成三個組件Application,Server,Middleware和協議中傳輸的內容。

將這三個組件對映射到我們具體使用的組件是:

Server:常用的有uWSGI,gunicorn等

Application:Django,Flask等

Middleware: Flask等框架中的裝飾器

點擊這裡查看官方關於WSGI協議的定義

組件Application

應用程序,是一個可重複調用的可調用對象,在Python中可以是一個函數,也可以是一個類,如果是類的話要實現__call__方法,要求這個可調用對象接收2個參數,返回一個內容結果

接收的2個參數分別是environ和start_response。

  • environ是web服務器解析HTTP協議的一些信息,例如請求方法,請求URI等信息構成的一個Dict對象。
  • start_response是一個函數,接收2個參數,一個是HTTP狀態碼,一個HTTP消息中的響應頭。

依照官方提供的示例用函數實現應用程序

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服務器,主要是實現相應的信息轉換,將網絡請求中的信息,按照HTTP協議將內容拿出,同時按照WSGI協議組裝成新的數據,同時將提供的start_response傳遞給Application。最後接收Application返回的內容,按照WSGI協議解析出。最終按照HTTP協議組織好內容返回就完成了一次請求。

Server操作的步驟如下:

  1. 根據HTTP協議內容構建envrion
  2. 提供一個start_response函數,接收HTTP STATU 和 HTTP HEADER
  3. 將envrion和start_response作為參數調用Application
  4. 接收Application返回的結果
  5. 按照HTTP協議,順序寫入HTTP響應頭(start_response接收),HTTP響應體(Application返回結果)

下面這個是pep3333協議中的一個server例子,按照CGI請求的方式來實現。

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):
\t# 按照WSGI協議,構建environ內容
\t# 1類 CGI相關的變量,此腳本就是用於cgi執行,所以前面的web服務器已經將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):
\t # 將內容返回
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()
\t
\t
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
\t
\t# 將上面處理的參數交給應用程序
result = application(environ, start_response)
try:
\t # 將請求到的結果寫回。
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函數,可以想start_response函數一樣接收HTTP STATU和Headers;和environ。

在服務端看來,他可以接收2個參數,並且可以返回一個類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

協議內容

重點看environ有哪些內容,這裡面才是瀏覽器每次請求時的信息。再深入一點探索,就是HTTP請求消息中的請求頭和請求體都是怎麼定義及怎麼回去的。

environ是一個字典,environ中要包含CGI定義的變量,主要是將HTTP協議中的內容,比如請求方法,POST/GET,請求URI等,另外是WSGI協議自己定義的變量,比如請求body中要讀取的信息等。列一下主要變量項如下:

CGI相關變量

變量說明REQUEST_METHODPOST,GET等,HTTP請求的動詞標識SERVER_PROTOCOL服務器運行的HTTP協議. 這裡當是HTTP/1.0.PATH_INFO附加的路徑信息, 由瀏覽器發出.QUERY_STRING請求URL的“?”後面的部分CONTENT_TYPEHTTP請求中任何Content-Type字段的內容CONTENT_LENGTH標準輸入口的字節數.HTTP_[變量]其他一些變量,例如HTTP_ACCEPT,HTTP_REFERER等

上述內容是動態開發的根基,只有根據上述內容才可以標準化的動態處理請求。

WSGI定義變量

變量說明wsgi.versionWSGI版本,要求是元組(1,0),標識WSGI 1.0協議wsgi.url_scheme表示調用應用程序的URL的協議,http或httpswsgi.input類文件對象,讀取HTTP請求體字節的輸入流wsgi.errors類文件對象,寫入錯誤輸出的輸出流wsgi.multithread如果是多線程,則設置為True,否則為False。wsgi.multiprocess如果是多進程,則設置為True,否則為False。wsgi.run_once如果只需要運行一次,設置為True

WSGI協議對於兩個輸入輸出流有一些方法必須要實現

流方法wsgi.inputread(size)wsgi.inputreadline()wsgi.inputreadlines(hint)wsgi.inputiter()wsgi.errorsflush()wsgi.errorswrite(str)wsgi.errorswritelines(seq)

這些基本上就是WSGI協議中定義的主要變量,也基本上涵蓋了我們開發時所需要的變量。

Server端按照協議的內容生成這些environ字典,然後將請求信息交給Application,Application根據這些信息確認請求要處理的內容,然後返回響應消息。從頭順下來就是這個流程。

示例展示

Server端涉及到實現http相關內容,我們直接使用python內置wsgiref來實現,具體代碼如下:

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")]
# 創建應用程序
app = ResponseTimingMiddleware(simple_app)
# 啟動服務,監聽8080
httpd = make_server('localhost', 8080, app)
httpd.serve_forever()

啟動服務後,我們打開瀏覽器訪問http://localhost:8080,執行結果如下。

"

Web應用程序開發

Web應用程序的本質是什麼

簡單描述Web應用程序的本質,就是我們通過瀏覽器訪問互聯網上指定的網頁文件展示到瀏覽器上。

流程如下圖:

Python學習教程:WEB開發——Python WSGI協議詳解

從更深層次一點的技術角度來看,由以下幾個步驟:

  • 瀏覽器,將要請求的內容按照HTTP協議發送服務端
  • 服務端,根據請求內容找到指定的HTML頁面
  • 瀏覽器,解析請求到的HTML內容展示出來

HTTP協議的全稱是HyperText Transfer Protocol(超文本傳輸協議)

HTTP協議是我們常用的五層協議中的應用層(5層從上到下是應用層,傳輸層,網絡層,數據鏈路層,物理層),HTTP協議中協定的內容稱之為消息,消息主要包括消息頭——Header和消息體——Body。

客戶端請求時的消息稱為Request,服務端響應時的消息稱為Response.

Header:包括請求方法,HTTP版本,URI,狀態碼,COOKIE等

Body:是響應或者請求時的內容,包含HTML,CSS,JS等

HTTP協議這裡就不做過多的描述,可以到點擊這裡深入瞭解HTTP協議

HTML的全稱是Hyper Text Markup Language(超文本標記語言)

簡單點說,HTML 是一種由不同元素組成的標記語言,它定義了網頁內容的含義和結構,所有我們在瀏覽器中看到的內容都是由一個一個的元素組成。除 HTML 以外的其它技術則通常用來描述一個網頁的表現與展示效果(如 CSS),或功能與行為(如 JavaScript)。

WEB開發的歷程

靜態開發

直接將寫好的HTML頁面放在服務器上,然後直接通過瀏覽器訪問指定服務器的文件。

動態開發

隨著我們的需求變化單獨使用靜態開發已經不能完全滿足我們。
例如我們查看的頁面只有部分內容會變化,那我們再去開發相同的頁面。
一是開發上是一種重複工作,完全是一種浪費。
二是數據量變化巨大時,完全是跟不上速度,並且數據變化也不是定時更新。
為了應對這種問題,動態網頁技術也就誕生了。早期的動態網頁開發技術是CGI
CGI全稱:Common Gateway Interface,通用網關接口,它是一段程序,運行在服務器上如:HTTP 服務器,
提供同客戶端 HTML 頁面的接口。
CGI 程序可以是 Python 腳本,PERL 腳本,SHELL 腳本,C 或者 C++ 程序等。
各種編程語言也針對動態網頁開發給出不同的解決方案,JAVA的servlet,Python的WSGI協議等。

Python的WSGI協議也是我們本章要講的內容

CGI流程

Python學習教程:WEB開發——Python WSGI協議詳解

WSGI的流程

Python學習教程:WEB開發——Python WSGI協議詳解

什麼是WSGI

WSGI全稱是Web Server Gateway Interface,其主要作用是Web服務器與Python Web應用程序或框架之間的建議標準接口,以促進跨各種Web服務器的Web應用程序可移植性。

WSGI並不是框架而只是一種協議,我們可以將WSGI協議分成三個組件Application,Server,Middleware和協議中傳輸的內容。

將這三個組件對映射到我們具體使用的組件是:

Server:常用的有uWSGI,gunicorn等

Application:Django,Flask等

Middleware: Flask等框架中的裝飾器

點擊這裡查看官方關於WSGI協議的定義

組件Application

應用程序,是一個可重複調用的可調用對象,在Python中可以是一個函數,也可以是一個類,如果是類的話要實現__call__方法,要求這個可調用對象接收2個參數,返回一個內容結果

接收的2個參數分別是environ和start_response。

  • environ是web服務器解析HTTP協議的一些信息,例如請求方法,請求URI等信息構成的一個Dict對象。
  • start_response是一個函數,接收2個參數,一個是HTTP狀態碼,一個HTTP消息中的響應頭。

依照官方提供的示例用函數實現應用程序

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服務器,主要是實現相應的信息轉換,將網絡請求中的信息,按照HTTP協議將內容拿出,同時按照WSGI協議組裝成新的數據,同時將提供的start_response傳遞給Application。最後接收Application返回的內容,按照WSGI協議解析出。最終按照HTTP協議組織好內容返回就完成了一次請求。

Server操作的步驟如下:

  1. 根據HTTP協議內容構建envrion
  2. 提供一個start_response函數,接收HTTP STATU 和 HTTP HEADER
  3. 將envrion和start_response作為參數調用Application
  4. 接收Application返回的結果
  5. 按照HTTP協議,順序寫入HTTP響應頭(start_response接收),HTTP響應體(Application返回結果)

下面這個是pep3333協議中的一個server例子,按照CGI請求的方式來實現。

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):
\t# 按照WSGI協議,構建environ內容
\t# 1類 CGI相關的變量,此腳本就是用於cgi執行,所以前面的web服務器已經將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):
\t # 將內容返回
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()
\t
\t
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
\t
\t# 將上面處理的參數交給應用程序
result = application(environ, start_response)
try:
\t # 將請求到的結果寫回。
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函數,可以想start_response函數一樣接收HTTP STATU和Headers;和environ。

在服務端看來,他可以接收2個參數,並且可以返回一個類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

協議內容

重點看environ有哪些內容,這裡面才是瀏覽器每次請求時的信息。再深入一點探索,就是HTTP請求消息中的請求頭和請求體都是怎麼定義及怎麼回去的。

environ是一個字典,environ中要包含CGI定義的變量,主要是將HTTP協議中的內容,比如請求方法,POST/GET,請求URI等,另外是WSGI協議自己定義的變量,比如請求body中要讀取的信息等。列一下主要變量項如下:

CGI相關變量

變量說明REQUEST_METHODPOST,GET等,HTTP請求的動詞標識SERVER_PROTOCOL服務器運行的HTTP協議. 這裡當是HTTP/1.0.PATH_INFO附加的路徑信息, 由瀏覽器發出.QUERY_STRING請求URL的“?”後面的部分CONTENT_TYPEHTTP請求中任何Content-Type字段的內容CONTENT_LENGTH標準輸入口的字節數.HTTP_[變量]其他一些變量,例如HTTP_ACCEPT,HTTP_REFERER等

上述內容是動態開發的根基,只有根據上述內容才可以標準化的動態處理請求。

WSGI定義變量

變量說明wsgi.versionWSGI版本,要求是元組(1,0),標識WSGI 1.0協議wsgi.url_scheme表示調用應用程序的URL的協議,http或httpswsgi.input類文件對象,讀取HTTP請求體字節的輸入流wsgi.errors類文件對象,寫入錯誤輸出的輸出流wsgi.multithread如果是多線程,則設置為True,否則為False。wsgi.multiprocess如果是多進程,則設置為True,否則為False。wsgi.run_once如果只需要運行一次,設置為True

WSGI協議對於兩個輸入輸出流有一些方法必須要實現

流方法wsgi.inputread(size)wsgi.inputreadline()wsgi.inputreadlines(hint)wsgi.inputiter()wsgi.errorsflush()wsgi.errorswrite(str)wsgi.errorswritelines(seq)

這些基本上就是WSGI協議中定義的主要變量,也基本上涵蓋了我們開發時所需要的變量。

Server端按照協議的內容生成這些environ字典,然後將請求信息交給Application,Application根據這些信息確認請求要處理的內容,然後返回響應消息。從頭順下來就是這個流程。

示例展示

Server端涉及到實現http相關內容,我們直接使用python內置wsgiref來實現,具體代碼如下:

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")]
# 創建應用程序
app = ResponseTimingMiddleware(simple_app)
# 啟動服務,監聽8080
httpd = make_server('localhost', 8080, app)
httpd.serve_forever()

啟動服務後,我們打開瀏覽器訪問http://localhost:8080,執行結果如下。

Python學習教程:WEB開發——Python WSGI協議詳解

上圖可以看到我們前面提到的中間件以及Application中執行返回的結果全都實現。

Python學習教程關於WSGI協議內容就到這,可以說是非常良心,非常詳細了!大夥兒,用心學哈!

"

相關推薦

推薦中...