python wsgi PEP333 中文翻譯


PEP 333 中文翻譯

首先說明一下,本人不是專門翻譯的,英文水平也不敢拿來獻丑。只是這是兩年前用python的時候為了自己學習方便而翻譯的,記錄着筆記自己看看而已。最近翻出來看看覺得還是放出來吧。一個網絡上還是沒有找到別人完整的中文翻譯,這個自己的爛翻譯就姑且放出來吧。也方便其他英文不好的人了,總比沒有光看英文好吧。

另外我翻譯之前有朋友其實已經翻譯了一半了(他的翻譯點這里http://blog.sina.com.cn/s/blog_8a18c33d01019xqb.html),但是沒全,所以我接上翻譯的后面,只為自己學習看的。前面大部分是拷貝他的翻譯來的,后面許多地方翻譯的很不好,請大家見諒。

 

如果有朋友願意指出錯誤來我很樂意修正過來。原文地址 http://www.python.org/dev/peps/pep-0333/

 

Preface 前言
----------------------------------------------------------------------------------

這個版本更新的版本支持Python 3.x以及一些其他的勘誤說明等。請參閱PEP 3333. 

 

Abstract 簡介

----------------------------------------------------------------------------------

本文檔描述一份在web服務器與web應用/web框架之間的標准接口,此接口的目的是使得web應用在不同web服務器之間具有可移植性。

 

Rationale and Goals 基本原理與目標

----------------------------------------------------------------------------------

python目前擁有大量的web框架,比如 Zope, Quixote, Webware, SkunkWeb, PSO, 和Twisted Web。大量的選擇使得新手無所適從,因為總得來說,框架的選擇都會限制web服務器的選擇。
 
對比之下,雖然java也擁有許多web框架,但是java的" servlet" API使得使用任何框架編寫出來的應用程序可以在任何支持" servlet" API的web服務器上運行。服務器中這種針對python的API(不管服務器是用python寫的,還是內嵌python,還是通過一種協議來啟動python)的使用和普及,將分離人們對web框架和對web服務器的選擇,用戶可以自由選擇適合他們的組合,而web服務器和web框架的開發者也能夠把精力集中到各自的領域。
 
因此,這份PEP建議在web服務器和web應用/web框架之間建立一種簡單的通用的接口規范,Python Web Server Gateway Interface (WSGI).
 
但是光有這么一份規范對於改變web服務器和web應用/框架的現狀是不夠的,只有web服務器和web框架的作者們實現WSGI,他才能起應有的效果。
 
然而,既然還沒有任何框架或服務器實現了WSGI,對實現WSGI也沒有什么直接的獎勵,那么WSGI必須容易實現,這樣才能降低作者的初始投資。
 
服務器和框架兩邊接口的實現的簡單性,對於WSGI的作用來說,絕對是非常重要的。所以這一點是任何設計決策的首要依據。
 
對於框架作者來說,實現的簡單和使用的方便是不一樣的。WSGI為框架作者展示一套絕對沒有"frills"的接口,因為象response對象和對cookie的處理這些問題和框架現有的對這些問題的處理是矛盾的。再次重申一遍,WSGI的目的是使得web框架和web服務器之間輕松互連,而不是創建一套新的web框架。
 
同時也要注意到,這個目標使得WSGI不能依賴任何在當前已部署版本的python沒有提供的任何功能,因此,也不會依賴於任何新的標准模塊,並且WSGI並不需要2.2.2以上版本的python(當然,在以后的python標准庫中內建支持這份接口的web服務器也是個不錯的主意)
 
不光要讓現有的或將要出現的框架和服務器容易實現,也應該容易創建請求預處理器、響應處理程序和其他基於WSGI的中間件組件,對於服務器來說他們是應用程序,而對於他們包含的應用程序來說他們是服務器。
 
如果中間件既簡單又健壯,而且WSGI廣泛得實現在服務器和框架中,那么就有可能出現全新的python web框架:整個框架都是由幾個WSGI中間件組件組成。甚至現有框架的作者都會選擇重構將以實現的服務以這種方式提供,變得更象一些和WSGI配合使用的庫而不是一個獨立的框架。這樣web應用開發這就可以根據特定功能選擇最適合的組件,而不是所有功能都由同一個框架提供。
 
當然,這一天無疑還要等很久,在這之間,一個合適的短期目標就是讓任何框架在任何服務器上運行起來。
 
最后,需要指出的是當前版本的WSGI並沒有規定一個應用具體以何種方式部署在web服務器或gateway上。目前,這個需要由服務器或gateway的具體實現來定義。如果足夠多實現了WSGI的服務器或gateway通過領域實踐產生了這個需求,也許可以產生另一份PEP來描述WSGI服務器和應用框架的部署標准。
 
Specification Overview  概述
----------------------------------------------------------------------------------
WSGI接口有兩種形式:一個是針對服務器或gateway的,另一個針對應用程序或框架。服務器接口請求一個由應用接口提供的可調用的對象,至於該對象是如何被請求的取決與服務器或gateway。我們假定一些服務器或gateway會需要應用程序的部署人員編寫一個簡短的腳本來啟動一個服務器或gateway的實例,並把應用程序對象提供得服務器,而其他的服務器或gateway需要配置文件或其他機制來指定從哪里導入或者或得應用程序對象。
 
除了純粹的服務器/gateway和應用程序/框架,還可以創建實現了這份規格說明書的中間件組件,對於包含他們的服務器他們是應用程序,而對於他們包含的應用程序來說他們是服務器,他們可以用來提供可擴展的API,內容轉換,導航和其他有用的功能。
 
在整個規格說明書中,我們使用短語"一個可調用者"意思是"一個函數,方法,類,或者擁有 __call__ 方法的一個對象實例",這取決與服務器,gateway,應用程序根據需要而選擇的合適實現方式。相反服務器,gateway和請求一個可調用者的應用程序不可以依賴具體的實現方式,not introspected upon.
The Application/Framework Side 應用程序/框架 端
----------------------------------------------------------------------------------
一個應用程序對象是一個簡單的接受兩個參數的可調用對象,這里的對象並不是真的需要一個對象實例,一個函數、方法、類、或者帶有 __call__ 方法的對象實例都可以用來做應用程序對象。應用程序對象必須可以多次被請求,實際上服務器/gateway(而非CGI)確實會產生這樣的重復請求。
 
(注意:雖然我們把他叫做"應用程序"對象,但並不是說程序員要把WSGI當做API來調用,我們假定應用程序開發者仍然使用更高層面上的框架服務來開發應用程序,WSGI是提供給框架和服務器開發者使用的工具,並不打算直接對應用程序開發者提供支持)
 
這里有兩個應用程序對象的示例,一個是函數,另一個是類:
 1 def simple_app(environ, start_response):
 2     """Simplest possible application object"""
 3     status = '200 OK'
 4     response_headers = [('Content-type', 'text/plain')]
 5     start_response(status, response_headers)
 6     return ['Hello world!\n']
 7 
 8 
 9 class AppClass:
10     """Produce the same output, but using a class
11 
12     (Note: 'AppClass' is the "application" here, so calling it
13     returns an instance of 'AppClass', which is then the iterable
14     return value of the "application callable" as required by
15     the spec.
16 
17     If we wanted to use *instances* of 'AppClass' as application
18     objects instead, we would have to implement a '__call__'
19     method, which would be invoked to execute the application,
20     and we would need to create an instance for use by the
21     server or gateway.
22     """
23 
24     def __init__(self, environ, start_response):
25         self.environ = environ
26         self.start = start_response
27 
28     def __iter__(self):
29         status = '200 OK'
30         response_headers = [('Content-type', 'text/plain')]
31         self.start(status, response_headers)
32         yield "Hello world!\n"
The Server/Gateway Side  服務器/網關 接口

----------------------------------------------------------------------------------

服務器/網關 為每一個http客戶端發來的請求都會請求應用程序可調用者一次。為了說明這里有一個CGI gateway,以一個獲取應用程序對象的函數實現,請注意,這個例子擁有有限的錯誤處理,因為默認情況下沒有被捕獲的異常都會被輸出到sys.stderr並被服務器記錄下來。

 1 import os, sys
 2 
 3 def run_with_cgi(application):
 4 
 5     environ = dict(os.environ.items())
 6     environ['wsgi.input']        = sys.stdin
 7     environ['wsgi.errors']       = sys.stderr
 8     environ['wsgi.version']      = (1, 0)
 9     environ['wsgi.multithread']  = False
10     environ['wsgi.multiprocess'] = True
11     environ['wsgi.run_once']     = True
12 
13     if environ.get('HTTPS', 'off') in ('on', '1'):
14         environ['wsgi.url_scheme'] = 'https'
15     else:
16         environ['wsgi.url_scheme'] = 'http'
17 
18     headers_set = []
19     headers_sent = []
20 
21     def write(data):
22         if not headers_set:
23              raise AssertionError("write() before start_response()")
24 
25         elif not headers_sent:
26              # Before the first output, send the stored headers
27              status, response_headers = headers_sent[:] = headers_set
28              sys.stdout.write('Status: %s\r\n' % status)
29              for header in response_headers:
30                  sys.stdout.write('%s: %s\r\n' % header)
31              sys.stdout.write('\r\n')
32 
33         sys.stdout.write(data)
34         sys.stdout.flush()
35 
36     def start_response(status, response_headers, exc_info=None):
37         if exc_info:
38             try:
39                 if headers_sent:
40                     # Re-raise original exception if headers sent
41                     raise exc_info[0], exc_info[1], exc_info[2]
42             finally:
43                 exc_info = None     # avoid dangling circular ref
44         elif headers_set:
45             raise AssertionError("Headers already set!")
46 
47         headers_set[:] = [status, response_headers]
48         return write
49 
50     result = application(environ, start_response)
51     try:
52         for data in result:
53             if data:    # don't send headers until body appears
54                 write(data)
55         if not headers_sent:
56             write('')   # send headers now if body was empty
57     finally:
58         if hasattr(result, 'close'):
59             result.close()
Middleware: Components that Play Both Sides  中間件 : 同時扮演兩種角色的組件
注意到單個對象可以作為請求應用程序的服務器存在,也可以作為被服務器調用的應用程序存在。這樣的中間件可以執行這樣一些功能:
 
  1.     重寫前面提到的 environ 之后,可以根據目標URL將請求傳遞到不同的應用程序對象
  2.     允許多個應用程序和框架在同一個進程中運行
  3.     通過在網絡傳遞請求和響應,實現負載均衡和遠程處理
  4.     對內容進行后加工,比如附加xsl樣式表
 
中間件的存在對於服務器接口和應用接口來說都應該是透明的,並且不需要特別的支持。希望在應用程序中加入中間件的用戶只需簡單得把中間件當作應用提供給服務器,並配置中間件足見以服務器的身份來請求應用程序。
 
當然,中間件組件包裹的可能是包裹應用程序的另一個中間件組件,這樣循環下去就構成了我們稱為"中間件堆棧"的東西了。for the most part,中間件要符合應用接口和服務器接口提出的一些限制和要求,有些時候這樣的限制甚至比純粹的服務器或應用程序還要嚴格,這些地方我們會特別指出。
 
這里有一個中間件組件的例子,他用Joe Strout的piglatin.py將text/plain的響應轉換成pig latin(注意:真正的中間件應該使用更加安全的方式——應該檢查內容的類型和內容的編碼,同樣這個簡單的例子還忽略了一個單詞might be split across a block boundary的可能性)。
 1 from piglatin import piglatin
 2 
 3 class LatinIter:
 4 
 5     """Transform iterated output to piglatin, if it's okay to do so
 6 
 7     Note that the "okayness" can change until the application yields
 8     its first non-empty string, so 'transform_ok' has to be a mutable
 9     truth value.
10     """
11 
12     def __init__(self, result, transform_ok):
13         if hasattr(result, 'close'):
14             self.close = result.close
15         self._next = iter(result).next
16         self.transform_ok = transform_ok
17 
18     def __iter__(self):
19         return self
20 
21     def next(self):
22         if self.transform_ok:
23             return piglatin(self._next())
24         else:
25             return self._next()
26 
27 class Latinator:
28 
29     # by default, don't transform output
30     transform = False
31 
32     def __init__(self, application):
33         self.application = application
34 
35     def __call__(self, environ, start_response):
36 
37         transform_ok = []
38 
39         def start_latin(status, response_headers, exc_info=None):
40 
41             # Reset ok flag, in case this is a repeat call
42             del transform_ok[:]
43 
44             for name, value in response_headers:
45                 if name.lower() == 'content-type' and value == 'text/plain':
46                     transform_ok.append(True)
47                     # Strip content-length if present, else it'll be wrong
48                     response_headers = [(name, value)
49                         for name, value in response_headers
50                             if name.lower() != 'content-length'
51                     ]
52                     break
53 
54             write = start_response(status, response_headers, exc_info)
55 
56             if transform_ok:
57                 def write_latin(data):
58                     write(piglatin(data))
59                 return write_latin
60             else:
61                 return write
62 
63         return LatinIter(self.application(environ, start_latin), transform_ok)
64 
65 
66 # Run foo_app under a Latinator's control, using the example CGI gateway
67 from foo_app import foo_app
68 run_with_cgi(Latinator(foo_app))
Specification Details 規格的詳細說明
----------------------------------------------------------------------------------

應用程序對象必須接受兩個參數,為了方便說明我們不妨分別命名為 environ 和 start_response ,但並非必須取這個名字。服務器或gateway必須用這兩個參數請求應用程序對象(比如象上面展示的,這樣調用 result = application(environ,start_response) )

參數 environ 是個字典對象,包含CGI風格的環境變量。這個對象必須是一個python內建的字典對象(不能是子類、UserDict或其他對字典對象的模仿),應用程序可以以任何他願意的方式修改這個字典, environ 還應該包含一些特定的WSGI需要的變量(在后面的節里會描述),有可以包含一些服務器特定的擴展變量,通過下面提高的約定命名。

start_response 參數是一個接受兩個必須參數和一個可選參數的可調用者。方便說明,我們分別把他們命名為 status, response_headers ,和 exc_info 。應用程序必須用這些參數來請求可調用者 start_response (比如象這樣 start_response(status,response_headers) )

參數 status 是一個形式象"999 Message here"的狀態字符串。而 response_headers 參數是元組(header_name,header_value)的列表,描述http響應頭。可選的 exc_info 參數會在下面的 `The start_response() Callable`_ 和 Error Handling 兩節中描述,他只有在應用程序產生了錯誤並希望在瀏覽器上顯示錯誤的時候才有用。

start_response 可調用者必須返回一個 write(body_data) 可調用者,他接受一個可選參數:一個將要被做為http響應體的一部分輸出的字符串(注意:提供可調用者 write() 只是為了支持現有框架的必要的輸出API,新的應用程序或框架盡量避免使用,詳細情況請看 Buffering and Streaming 一節。)

當被服務器請求的時候,應用程序對象必須返回一個0或多個可迭代的字符串,這可以通過多種方法完成,比如返回一個字符串的列表,或者應用程序本身是一個生產字符串的函數,或者應用程序是一個類而他的實例是可迭代的,不管怎么完成,應用程序對象必須總是返回0或多個可迭代的字符串。

服務器必須將產生的字符串以一種無緩沖的方式傳送到客戶端,每次傳完一個字符串再去獲取下一個。(換句話說,應用程序應該實現自己的緩沖,更多關於應用程序輸出必須如何處理的細節請閱讀下面的 Buffering and Streaming 節。)

服務器或gateway應該把產生的字符串當字節流對待:特別地,他必須保證沒修改行的結尾。應用程序負責確保字符串是以與客戶端匹配的編碼輸出(服務器/gateway可能會附加HTTP傳送編碼,或者為了實現一些http的特性而進行一些轉換比如byte-range transmission,更多細節請看下面的 Other HTTP Features )

如果調 len(iterable) 成功,服務器將認為結果是正確的。也就是說,應用程序返回的可迭代的字符串提供了一個有用 的__len__() 方法,么肯定返回了正確的結果(關於這個方法正常情況下如何被使用的請閱讀 Handling the Content-Length Header )

如果應用程序返回的可迭代者有close()方法,則不管該請求是正常結束還是由於錯誤而終止,服務器/gateway都**必須**在結束該請求之前調用這個方法,(這是用來支持應用程序對資源的釋放,This protocol is intended to complement PEP 325's generator support, and other common iterables with close() methods.)

(注意:應用程序必須在可迭代者產生第一個字符串之間請求 start_response() 可調用者,這樣服務器才能在發送任何主體內容之前發送響應頭,然而這一步也可以在可迭代者第一次迭代的時候執行,所以服務器不能假定開始迭代之前 start_response() 已經被調用過了)

最后,服務器或gateway不能應用程序返回的可迭代者的任何其他屬性,除非是針對服務器或gateway特定類型的實例,比如wsgi.file_wrapper返回的“file wrapper”(閱讀 Optional Platform-Specific File Handling )。通常情況下,只有在這里指定的屬性,或者通過PEP 234 iteration APIs才是可以訪問的。

environ Variables environ變量
environ 字典被用來包含這些在Common Gateway Interface specification [2]_中定義了的CGI環境變量。下面這些變量 必須 呈現出來, 除非其值是空字符串,這種情況下如果下面沒有特別指出的話他們 可能 會被忽略
 
REQUEST_METHOD
    HTTP請求的方式, 比如 "GET" 或者 "POST". 這個不可能是空字符串並且也是必須給出的。
SCRIPT_NAME
    請求URL中路徑的開始部分,對應應用程序對象,這樣應用程序就知道它的虛擬位置。如果該應用程序對應服務器的 根 的話, 它 可能 是為空字符串。
PATH_INFO
    請求URL中路徑的剩余部分,指定請求的目標在應用程序內部的虛擬位置。如果請求的目標是應用程序跟並且沒有trailing slash的話,可能為空字符串 。
QUERY_STRING
    請求URL中跟在"?"后面的那部分,可能為空或不存在.
CONTENT_TYPE
    HTTP請求中任何 Content-Type 域的內容。
CONTENT_LENGTH
    HTTP請求中任何 Content-Length 域的內容。可能為空或不存在.
SERVER_NAME, SERVER_PORT
    這些變量可以和 SCRIPT_NAME、PATH_INFO 一起組成完整的URL。然而要注意的是,重建請求URL的時候應該優先使用 HTTP_HOST 而非 SERVER_NAME 。詳細內容請閱讀下面的 URL Reconstruction 。 SERVER_NAME 和 SERVER_PORT 永遠是空字符串,也總是必須存在的。
SERVER_PROTOCOL
    客戶端發送請求所使用協議的版本。通常是類似 "HTTP/1.0" 或 "HTTP/1.1" 的東西可以被用來判斷如何處理請求headers。(既然這個變量表示的是請求中使用的協議,而且和服務器響應時使用的協議無關,也許它應該被叫做 REQUEST_PROTOCOL 。然后,為了保持和CGI的兼容性,我們還是使用已有的名字。)
HTTP_ 變量
    對應客戶端提供的HTTP請求headers (也就是說名字以 "HTTP_" 開頭的變量)。這些變量的存在與否應該和請求中的合適的HTTP header一致。
 
服務器或gateway 應該 盡可能提供其他可用的CGI變量。另外,如果用了SSL,服務器或gateway也 應該 盡可能提供可用的Apache SSL環境變量 [5] ,比如 HTTPS=on 和SSL_PROTOCOL``。不過要注意,使用了任何上面沒有列出的變量的應用程序對不支持相關擴展的服務器來說就有點necessarily non-portable。(比如,不發布文件的web服務器就不能提供一個有意義的 ``DOCUMENT_ROOT 或 PATH_TRANSLATED 。)
 
一個支持WSGI的服務器或gateway 應該 在描述它們自己的同時說明它們可以提供些什么變量應用程序 應該 對所有他們需要的變量的存在性進行檢查,並且在某變量不存在的時候有備用的措施
 
注意: 不需要的變量 (比如在不需要驗證的情況下的 REMOTE_USER ) 應該被移出 environ字典。同樣注意CGI定義的變量如果存在的話必須是字符串。任何 str 類型以外的CGI變量的存在都是對本規范的違反
 
除了CGI定義的變量, environ 字典也可以包含任意操作系統的環境變量,並且 必須包含下面這些WSGI定義的變量:
 
 
 
 
Variable Value
wsgi.version The tuple (1, 0), representing WSGI version 1.0.
wsgi.url_scheme A string representing the "scheme" portion of the URL at which the application is being invoked. Normally, this will have the value "http" or "https", as appropriate.
wsgi.input An input stream (file-like object) from which the HTTP request body can be read. (The server or gateway may perform reads on-demand as requested by the application, or it may pre- read the client's request body and buffer it in-memory or on disk, or use any other technique for providing such an input stream, according to its preference.)
wsgi.errors

An output stream (file-like object) to which error output can be written, for the purpose of recording program or other errors in a standardized and possibly centralized location. This should be a "text mode" stream; i.e., applications should use "\n" as a line ending, and assume that it will be converted to the correct line ending by the server/gateway.

For many servers, wsgi.errors will be the server's main error log. Alternatively, this may be sys.stderr, or a log file of some sort. The server's documentation should include an explanation of how to configure this or where to find the recorded output. A server or gateway may supply different error streams to different applications, if this is desired.

wsgi.multithread This value should evaluate true if the application object may be simultaneously invoked by another thread in the same process, and should evaluate false otherwise.
wsgi.multiprocess This value should evaluate true if an equivalent application object may be simultaneously invoked by another process, and should evaluate false otherwise.
wsgi.run_once This value should evaluate true if the server or gateway expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a gateway based on CGI (or something similar).

最后 environ 字典也可以包含服務器定義的變量。這些變量的名字必須是小寫字母、數字、點和下划線,並且應該帶一個能唯一代表服務器或gateway的前綴。比如, mod_python 可能會定義象這樣的一些變量:mod_python.some_variable.

input and Error Streams 輸入和錯誤流

服務器提供的輸入輸出流必須提供以下的方法

Method Stream Notes
read(size) input 1
readline() input 1, 2
readlines(hint) input 1, 3
__iter__() input  
flush() errors 4
write(str) errors  
writelines(seq) errors  

 

 

 

 

 

 

以上方法語義同Python Library Reference相同,處理下面列表指出的注意點之外。

1.服務器不一定需要讀取客戶端指定的Content-length, 它可以模擬一個流結束(end-of-file)的條件,如果應用程序
試圖去讀取它的話。另外應用程序不應該去嘗試讀取比指定Content-length長度更多的數據。
2.可選參數size是不支持用於readline()方法中的。因為它有可能是服務器程序作者復雜的實現,而且它不怎么用的上。
3.注意這個用戶readlines(hint)中的參數hint,對於調用方和實現方都是可選的。
應用程序可以自由選擇是否支持,服務器也可以自由選擇是否忽略它。
4.由於錯誤流可能不能回轉(rewound),服務器和網關可以立即自由的不需要緩存地向前寫(forward write)。在這種情況下
flush()方法可能是個空操作(no-op)。然后可移植的程序不能假定這個output是無緩沖的或者flush是空操作。
在需要確保數據流被真正寫入的情況下,他們大多都會調用flush()。
(例如:從多個進程中寫入同一個日志文件的時候,可以做到最小化的數據交織)
 
上表列出的方法,每個服務器都必須符合此規范。應用程序方也需符合此規范,不要用其他的其他不同方法或屬性的input 和 errors對象。
尤其,應用程序端不要試圖去關閉這些流,盡管他們有close()方法。
The start_response() Callable 
傳遞給應用程序的第二個參數是一個可調用的形式,start_response(status, reponse_headers, exc_info=None).(同所有的WSGI調用,參數位置必須是
通過位置對應,而不是用key來對應)。
start_response調用被用來開始HTTP相應,並且必須返回一個 write(body_data) callable(參考下面的buffering and streaming段)
 
此status參數是http的'status'字符,如"200 OK", "404 Not Found".也就是說,它是一個字符串組成的一個狀態碼和一個原因短語,按這樣的順序並用個空格分隔。
兩頭不包含其他的字符或空格。(見RFC2616, 6.1.1段獲取更多信息),字符串不能包含控制字符,不能有終止符或換行符等其他組合結束的符號。
 
response_headers參數是個tuples(header_name, header_value)的列表list,必須是個python的list,即type(response_headers) is ListType.
並且服務器可以隨意更改其中的內容如果它需要的話,所有的header_name必須是合法的HTTP header field-name(參見RFC2616 4.2段),沒有冒號或其他標點。
所有的header_value不能包含任何控制字符,包括回車和換行。(這樣的要求是為了方便那些必須檢查相應頭的服務器,網關,中間件,使他們所必須的解析工作復雜度降到最低)
 
一般來說,服務器或網關負責確保正確的頭信息發送到客戶端,如果應用程序(application)遺漏了必要的頭信息(或其他相關的規范信息),服務器或網關須補上。
比如:HTTP date:和Server:頭信息通常是由服務器或網關提供。
(一個要提醒給服務器/網關作者的事情: HTTP 頭名稱是區分大小寫的,所以在檢查application提供的頭信息時一定要考慮大小寫的問題)
 
應用程序和中間件禁止使用HTTP/1.1的'hop-by-hop'特性或頭信息,任何在HTTP/1.0中等價或類似的特性或頭信息,都會影響到客戶端和服務器的持久的連接。
這特性專屬的目前的服務器,服務器/網關須要考慮考慮這一個致命錯誤,一個應用程序嘗試發送它們,並拋出一個錯誤,如果他們是提供給start_response()。
(為了解更多'hop-by-hop'的細節和特性,請參閱下面的Other HTTP Features段)。
 
start_response回調必須不實際地傳輸response頭,代替的,它用來存貯頭信息供服務器/網關傳輸,只有在應用程序第一次返回后第一次迭代的時候返回一個非空字符串,
或者在應用程序的一個調用write()回調的時候。換句話說,response頭不能被發送在沒有實際的body數據前,或者是應用程序返回的迭代器終止前,(唯一的例外就是response頭信息里明確包含了Content-Length為0)
這樣延遲的頭信息傳輸是為了確保有緩存或異步的應用程序能用出錯信息替換掉原來可能要發送的數據,直到最后一刻。例如應用程序可能會替換到頭狀態'200 OK'為'500 Internal Error', 如果當body數據是有應用程序緩存構成的但發送了錯誤。
 
exc_info參數是可選的,但如果被提供,必須是python sys.exc_info()tuple.此參數只有在start_response請求錯誤handler才要求被提供。如果exc_info提供了,並且還沒有任何HTTP header被輸出,start_response應該替換the currently-stored HTTP response headers with the newly-supplied ones,應此允許應用程序在有錯誤發生的情況下能夠改變output。
然而,如果exc_info被提供,並且HTTP頭已經被發送,start_response必須拋出錯誤,並且拋出exc_info tuple,也就是下面的
raise exc_info[0], exc_info[1], exc_info[2]
拋出的異常會被應用程序重新捕獲到,原則上應到終止應用程序。(應用程序會嘗試發送error output到瀏覽器,一旦HTTP headers被發送,這樣是不安全的)應用程序必須不捕獲任何來自於start_response調用的異常,如果調用start_response用來exc_info參數,替代的做法應該允許這樣的異常傳回給服務器/網關。更多信息見下面的Error Handing。
 
應用程序可能多次調用start_response當且僅當exc_info參數提供的時候。更確切的說,如果start_response已經被當且應用程序調用過后,再次調用沒有exc_info參數的start_response是個很致命的錯誤。(參考上面CGI網關示例,其中說明了正確的邏輯)
注意:服務器/網關/中間件實現start_response應當確保exc_info沒有持續指向任何引用,當start_response方法調用完成之后。為了避免通過traceback和frames involved創建了回環的引用,最簡單的例子如下。
def start_response(status, response_headers, exc_info=None):
    if exc_info:
         try:
             # do stuff w/exc_info here
         finally:
             exc_info = None    # Avoid circular ref.

示例CGI網關例子提供了這種技術的另一個說明。

 
Handling the Content-Length Header 處理Content-Length頭信息
如果應用程序沒有提供Content-Length頭,服務器/網關可以選擇幾種方式之一來處理它,最簡單的方式便是當response完成的時候關閉客戶端連接。
然而在某些情況下,服務器/網關之一可能會生成Content-Length頭,或者至少避免了要關閉客戶端連接,如果應用程序沒有調用write(),並返回一個len()是1的iterable,
那么服務器便可以自動的確定Content-Length長度,通過第一個iterable出來的字符串。
如果服務器和客戶段都支持HTTP/1.1 "chunked encoding",那么服務器可能在調用write()或迭代字符串的時候使用chunked encoding來發送塊數據,因此會為每個chunk塊數據生成Content-Length。這允許服務器保持客戶端長連接如果需要的話,注意如果真要這么做的話,服務器必須完成符號RFC2616規范,否則便回到了另外的策略上來處理Content-Length的缺失。
 
(注意:應用程序必須不能提供適合任何類型的Transfer-Encoding輸出,像chunking or gzipping,as "hop-by-hop"操作,這些編碼都是實際的服務器/網關的職權。詳細信息參見下面的Other HTTP Features段)
Buffering and Streaming  緩沖和流
一般而言,應用程序都會通過緩存output並且一次性發送來提高吞吐量。現有的Zope框架就有常見的處理方法:它通過緩存output到StringIO或類似的對象里面,並且順着response相應頭一次性發送.
在WSGI中相應的處理方法是應用程序簡單地返回一個單元素迭代(single-element iterable)如列表list,其中包含單一的response body字符串。這是對於絕大多數應用程序都推薦的方案,渲染HTML頁面的文本很容易停留在內存中。
然后對於大文件或專門用途的HTTP流媒體(如“服務器推送”),應用程序或許需要提供小塊狀的輸出(比如為了避免加載一個大的文件到內存中),有些時候部分的response可能需要花費多的時間來生成,但是再它之前發送一部分的數據還是很有用的。
這種情況下,應用程序通常會返回一個可迭代的(通常是迭代生成器),使生成的輸出是一塊一塊的。這些塊大小可能會超過最大的邊界(for 'server push'),或者在某個耗時的任務之前(比如在磁盤中讀取其他塊)。
WSGI服務器/網關和中間件不能延遲傳輸任何塊。他們要么完全將所有的塊都傳輸給客戶端,或者保證他們會繼續傳輸即使應用程序正在生成下一個塊。服務器/網關或中間件可能會提供以下三種方案中的一種。
1.發送整個塊到操作系統(要求任何的O/S緩存被刷新)在返回控制之前給應用程序
2.使用不同的線程確保塊被繼續傳輸,而應用程序生成下一個塊。
3.(僅中間件)發送整個塊到它的父服務器/網關
 
通過提供這樣的保證措施,WSGI就可以允許應用程序確保在他們輸出數據的任意點上不會變得停滯。這對正常允許是至關重要的,例如多媒體服務器推送流,多個邊界間的數據應該被完整的傳輸到客戶端。
Middleware Handling of Block Boundaries 中間件處理塊邊界
 
為了更好地支持異步應用程序和服務器,中間件必須不是塊迭代從應用程序迭代中等待多個值。如果中間件需要在自己能生成輸出流之前積攢從應用程序中的值,那么它必須yield一個空字符串。
或者達到這種需求的其他方式,一個中間件必須每次在它底層應用程序生成一個值的時候至少生成一個值,如果中間件不能成才其他值那么它必須生成一個空字符串。
這樣的要求確保了異步的服務器和應用程序能共同協作,在需要同時提供多個應用程序實例的時候減少線程的數量。
注意,這樣的要求意味着中間件必須返回一個iterable一旦其底層應用程序返回一個iterable。它也不允許中間件調用write()方法來發送數據。中間件僅可能用它父服務器的write()方法來傳輸數據,它底層的應用程序利用中間件提供的write()發送數據。
The write() Callable 
一些現有框架Apis與WSGI的一個不同處理方式是他們支持無緩存的輸出,特別指出的是,他們提供一個write函數或方法來寫一個無緩沖的塊或數據,或者他們提供一個緩沖的write函數和一個flush機制來flush緩沖。
不幸的是,這樣的APIS無法實現像WSGI這樣應用程序可迭代返回值,除非使用多線程或其他的機制。
因此為了允許這些框架繼續使用這些必要的API,WSGI包含一個特殊的write()調用,由start_response調用返回。
新的WSGI應用程序和框架不應該使用write()調用如果可以避免這樣做。這個write()調用是完全hack用來支持必要的流能力的apis。一般來說,應用程序應該通過返回的iterable來生成輸出流,因為這樣可以讓web服務器交錯在同一個Python的其他線程上,總的來說可以為服務器提供更大的吞吐量。
這個write()調用是由start_response調用返回的,並且它接受一個單一的參數:一個將用來寫入到HTTP 一部分response體中的字符串,它被看作是已經被迭代生成后的結果。換句話說,在writer()返回前,它必須保證傳入的字符串要么是完全發送給客戶機, 或者已經緩沖了應用程序要向前的傳輸。( or that it is buffered for transmission while the application proceeds onward
一個應用程序必須返回一個可迭代的對象,即使它使用write()來生成全部或部分response body。返回的可迭代可以是空的(例如一個空字符串),但是如果它不生成空字符串,那么output必須被處理,通常是由服務器/網關來做(例如它必須立即發送或加入隊列中)。應用程序必須不在他們返回的iterable內調用write()。因此所有由iterable生成的字符串會在傳遞給write()之后發送到客戶端。
 
Unicode Issues  Unicode問題

 

HTTP不直接支持Unicode,並且也沒有這樣的接口。所有的編碼解碼必須由應用程序來處理,所有傳遞給服務器的都必須是python標准的字節字符串而不是Unicode對象。其結果使用Unicode對象,一個字符串對象是必需的,是未定義的。

 

注意 所有的作為response頭的字符傳遞到start_response必須遵循RFC2616的編碼,也就是它們必須是ISO-8859-1字符,或者使用RFC 2047 MIME編碼。

 

python平台的str或StringType實際上是基於Unicode(如jython,ironPython, python 3000等),所有的字符串都只包含ISO-8859-1編碼規范中可表示的代碼點(從\u0000-\u00FF)。如果應用程序提供的字符串包含任何Unicode字符或編碼點,這將是個致命的錯誤。同樣地,服務器/網關也必須不提供任何Unicode字符到應用程序。

 

再次聲明,本規定所有的字符串都必須是str或StringType,並且不能是unicode或UnicodeType。盡管一個開發的平台允許超過8位字節的字符對象,也僅僅是低8位的字節被用到,對於本規范所指定的字符串來說。
Error Handling  錯誤處理
一般來說,應用程序應到捕獲自己的內部錯誤,並顯示有用的信息到瀏覽器上。(由瀏覽器來決定哪些信息是有用的)
然后要顯示這些信息,應用程序不能實際地發送任何數據到瀏覽器上,否則會使response惡化。因此WSGI提供了一種機制,要么允許應用程序發送它自己的錯誤信息,要不自動終止:這個exc_info參數到start_response。這里有個如何使用的例子。
 1 try:
 2     # regular application code here
 3     status = "200 Froody"
 4     response_headers = [("content-type", "text/plain")]
 5     start_response(status, response_headers)
 6     return ["normal body goes here"]
 7 except:
 8     # XXX should trap runtime issues like MemoryError, KeyboardInterrupt
 9     #     in a separate handler before this bare 'except:'...
10     status = "500 Oops"
11     response_headers = [("content-type", "text/plain")]
12     start_response(status, response_headers, sys.exc_info())
13     return ["error body goes here"]
如果當有異常發送的時候沒有信息被寫入,調用start_response將正常返回,並且應用程序返回的包含錯誤信息的body將被發送到瀏覽器。然而如果有任何的output被發送到瀏覽器,start_response會重新拋出提供的異常。此異常不應該被應用程序捕獲,並且應用程序會終止。服務器/網關會捕獲異常這個致命的異常並終止響應。
服務器應該捕獲任何異常並記錄日志當終止應用程序或反復迭代他們的返回值。如果當應用程序出錯的時候已經有一部分response被發送到瀏覽器了,服務器或網關可以試圖添加一個錯誤消息到output,如果已經發送了指明text/*的頭信息,那么服務器就知道如何修改清理。
一些中間件或許希望提供額外的異常處理服務,或攔截並替換應用程序的錯誤信息。在此情況下,中間件可能選擇不再重新拋出提供的exc_info到start_response,但換作拋出中間件特制的錯誤,或者在存貯了提供的參數后簡單地返回且不包含錯誤。這將導致應用程序返回錯誤的body iterable(或調用write()).允許中間件來攔截並修改錯誤流,這些技術只會工作只要應用程序作者做到:
1.總是提供exc_info當開始一個錯誤的響應的時候。
2.當exc_info已經提供的情況下不要捕獲來自start_response的異常。
HTTP 1.1 Expect/Continue  Http1.1的長連接

實現了HTTP1.1的服務器/網關必須提供對HTTP1.1中"expect/continue"(長連接)的透明支持。可以用下面的幾種方式完成。
1.用一個立即執行的"100 Continue"response相應一個包含Expect: 100-continue的請求,並且正常處理
2.正常處理請求,但提供給應用程序一個wsgi.input流,當應用程序首先嘗試從輸入流中讀取的時候發送一個"100 Continue"響應。這個讀取必須保持阻塞至到客戶端的響應。
3.等待客戶端判斷服務器不支持expect/continue以及客戶端自身發來的request body。(不好的方式,不推薦)
注意這些行為的限制不適用於HTTTP 1.O,或請求不是針對特定的應用程序對象。更多HTTP 1.1 Except/Continue的信息,請參閱RFC 2616的8.2.3段和10.1.1段。

Other HTTP Features 其他的HTTP特性
通常來說,應用程序對其的output有完全的控制權,服務器/網關對此因不理不問,他們只做一些不影響應用程序響應語義的修改。往往很可能的是為應用程序開發者提供中間件來支持一些特性,所以服務器/網關開發者在他們實現的時候應當偏保守。在某種意義上,一個服務器應當使自己更像是一個HTTP網關服務器,配合一個應用程序來作為一個HTTP "origin server"(這些定義參加RFC 2616 1.3段)
 
然后由於WSGI服務器和應用程序不通過HTTP通信,RFC 2616提到的"hop-by-hop"在WSGI內部通信中不適用。WSGI應用程序必須不生成任何"hop-by-hop"頭信息,試圖使用HTTP的特性需要他們生成這樣的頭,或者依賴任何傳入的"hop-by-hop"頭內容在environ字典中。WSGI服務器必須自己處理掉任何被支持入站的"hop-by-hop"頭信息,像such as by decoding any inbound  Transfer-Encoding, including chunked encoding if applicable。
 
將這些原則應用到各種各樣的HTTP特性中去,應當清楚一個服務器可能需要處理緩存驗證這些If-None-Match和If-Modified-Sine的請求頭,和一些Last-Modified和ETag響應頭。然而它並不是必須要這樣做,並且一個應用程序應到處理自己的緩存驗證等,如果它自身相提供這樣的特性,然后服務器/網關就不需要做這樣的驗證。
 
同樣地,服務器可能會重新編碼或傳輸編碼一個應用程序的響應,但是應用程序應當對自己發送的內容做適當的編碼,並且不提供transport encoding。如果客戶端需要服務器可能以字節碼的方式傳輸應用程序的響應,並且應用程序本身是不支持字節碼方式。再次申明,如果需要,應用程序應當自己執行相應的方法。
 
注意,這些在應用程序上的限制不是要求應用程序為每個HTTP特性重新實現一次,許多HTTP特性可以完全或部分地由中間件來實現,這樣便可以讓服務器和應用程序作者在一遍又一遍的實現這些特性中解放出來。
Thread Support  線程支持

線程的支持或缺乏,也是服務器依賴。服務器可以同時運行多個請求,也應當提供讓應用程序運行在單一線程上的選項,因此讓一些不是線程安全的應用程序或框架仍舊可以在這些服務器上使用。

Implementation/Application Notes  實現/應用 事項
Server Extension APIs  服務擴展API
有些服務器作者可能希望揭示更多的高級API,應用程序和框架作者便可以專門的使用。例如一個以mod_python為基礎的網關可能就希望揭示一些Apache的API作為WSGI的擴展。
在最簡單的情況下,它只需要定義一個environ變量,類似mod_python.some_api。但是,更多情況下,可能存在的中間件會使此變得更困難。例如,一個API提供的在environ變量中訪問的HTTP 頭,可能會被中間件修改掉而返回不一樣的值。
 
通常,任何擴展的API,重復,取代或者繞過部分WSGI的功能與中間件會有不兼容的風險。服務器/網關開發者不應當假設沒人會使用中間件,因為一些框架作者專門的打算重新改寫或組織他們的框架代碼,使之幾乎就像是各種各樣的中間件。
 
所以,為了提供最大的兼容性,服務器/網關提供一些擴展的API,取代一些WSGI的功能,必須設計這些API以便他們使用部分替換過的API調用。例如:一個允許訪問HTTP請求頭的擴展API需要求應用程序通過當前的environ,所以服務器/網關可能會驗證那些可被訪問的HTTP頭,保證沒有被中間件修改過。如果這擴展API不能保證,它總是evniron和HTTP頭信息里面的內容,它必須拒絕向應用程序提供服務。例如,通過拋出一個錯誤,返回None代替頭信息的收集等等,是適合API的。
 
同樣地,如果擴展API提供交替的方法來寫response數據或是頭信息,它應當要求start_response調用在應用程序能獲得的擴展的服務之前已被傳入。如果傳入的對象和最開始服務器/網關提供給應用程序的不一樣,它不能保證正確的操作並且必須拒絕給應用程序提供擴展的服務。
 
這些指導方針同樣適用於中間件,添加類似解析過的cookies信息,表單變量,sessions,或者像evniron。特別地,這樣的中間件應當使提供這些的特性類似一個在environ里操作的函數,而不是簡單的想evniron里面添加些東西。這些有助於保證來自evniron里面的信息計算是在任何中間件對URL重寫過后或者是其他evniron變量的修改之后。
 
或許以后一些中間件作者為了保證應用程序在使用他們的中間件過程中不被跳過,他們不得不從environ中刪除全部或部分的擴展API. 為了避免這些,這些安全的規則對所有的服務器/網關和中間件作者來說都是非常重要的。
Application Configuration  應用程序結構配置
這些規格說明沒有定義服務器如何選擇如何得到一個應用程序來調用。這些和其他一些配置選項都是高度地服務於服務器的。這些期望服務器/網關作者能講如何配置選項(如線程的選項),如何配置執行一個特定的應用程序對象等文檔化。
 
另一方面,框架作者應當將如何創建一個被框架功能包裝過的應用程序對象文檔化。用戶可以選擇服務器和應用程序框架,但必須將兩者連接在一起。然而,因為現在有了兩者共同的接口,這應該只是一個機械式的問題,而不是為了將新的應用程序或服務器配對組合的重大工程了。
 
最后,一些應用程序,框架,中間件可能希望使用evniron字典來接受一些簡單的字符串配置選項。服務器/網關應當通過允許應用程序部署者向evniron里面指定特殊的名值對來支持這些。在最簡單的情況下,這些支持可以僅僅由拷貝操作系統提供的環境變量os.environ到environ字典中,那么部署者原則上可以配置這些外部的信息到服務器上,或者在CGI的情況下他們可能是通過服務器的配置文件來設置。
 
應用程序應該保持這些變量的依賴到最小程度,並不是所有的服務器都支持簡單地配置它們。當然,即使在最槽糕的情況下,部署一個應用程序可以創建一個腳本來提供一些必要的選項值:
from the_app import application

def new_app(environ, start_response):
    environ['the_app.configval1'] = 'something'
    return application(environ, start_response)

但是,大多存在的應用程序和框架可能只需要從environ里面一個單一的配置值,用來指示它們的應用程序或框架專門的配置文件位置。(當然,應用程序應當緩存這些配置,以避免每次調用都重復讀取)

URL Reconstruction  URL的構建

如果應用程序希望改造請求的完整URL,可以使用如下的算法,由lan Bicking提供

 

 1 from urllib import quote
 2 url = environ['wsgi.url_scheme']+'://'
 3 
 4 if environ.get('HTTP_HOST'):
 5     url += environ['HTTP_HOST']
 6 else:
 7     url += environ['SERVER_NAME']
 8 
 9     if environ['wsgi.url_scheme'] == 'https':
10         if environ['SERVER_PORT'] != '443':
11            url += ':' + environ['SERVER_PORT']
12     else:
13         if environ['SERVER_PORT'] != '80':
14            url += ':' + environ['SERVER_PORT']
15 
16 url += quote(environ.get('SCRIPT_NAME', ''))
17 url += quote(environ.get('PATH_INFO', ''))
18 if environ.get('QUERY_STRING'):
19     url += '?' + environ['QUERY_STRING']
Supporting Older (<2.2) Versions of Python 支持老版本的Python(< 2.2)

略(沒翻譯,也沒看)

Optional Platform-Specific File Handling 可選的特別的平台文件處理

一些操作環境提供高性能的文件傳輸設施,像Unix的sendfile()方法。服務器和網關可能會通過environ中的wsgi.file_wrapper選項來揭示這功能。用於程序可以使用這樣的文件包裝來轉換文件或類文件對象到他們返回的迭代中去。例如:

if 'wsgi.file_wrapper' in environ:
    return environ['wsgi.file_wrapper'](filelike, block_size)
else:
    return iter(lambda: filelike.read(block_size), '')
如果服務器或網關提供wsgi.file_wrapper,它必須是個可調用的,並且接受一個必要的位置參數,和一個可選的位置參數。第一個參數是將被發送的類文件對象,第二個參數是可選的,標示分快大小的建議(這個服務器/網關不需要)。這個調用必須返回一個可迭代的對象,並且不能執行任何的數據傳輸直到服務器/網關真正接受到了作為應用程序返回值的迭代對象(否則會阻止中間件解析或覆蓋響應體)。
 
由應用程序提供的被認為是類文件的對象必須有可選大小參數的read()方法。它或許有close()方法,如果有,那么wsgi.file_wrapper必須有一個會調用類文件對象原始close()方法的close()方法。如果類文件對象有任何的方法或屬性與Python內置的文件對象的屬性或方法名相同(例如fileno()),那么wsgi.file_warpper可能會假定這些方法和屬性與Python內置的語義是相同的。
 
實際在任何特定平台上實現的文件處理必須發生在應用程序返回之后,並且服務器/網關檢查是否是一個包裝對象被返回。(再次聲明,因為存在的中間件,錯誤處理等等類似的東西,它不保證任何生成的包裝會被實際使用)
 
除了處理close(),從語義上講,應用程序返回一個包裝的文件應該和返回iter(filelike.read, '')一樣。換句話說,當傳輸開始的時候,應當從文件的當前位置開始傳輸,並且繼續直到最后完成。
 
當然,特定平台的文件傳輸API通常不接受隨意的類文件對象,所以,一個wsgi.file_wrapper為了判斷類文件對象是否適應於他們特定的平台,不得不對提供的對象做一些像fileno()(Unix-like OSes)或者是java.nio.FileChannel(在Jython下)的自省檢查。
 
注意:即使對象不適用與特定的平台API,wsgi.file_wrapper必須仍舊返回一個包裝了read()和close()的迭代,因此應用程序使用這文件包裝器便可以再不同平台間移植。這里有個簡單的平台無關的文件包裝類,適應於老(2.2之前)的和新的python,如下:
class FileWrapper:

    def __init__(self, filelike, blksize=8192):
        self.filelike = filelike
        self.blksize = blksize
        if hasattr(filelike, 'close'):
            self.close = filelike.close

    def __getitem__(self, key):
        data = self.filelike.read(self.blksize)
        if data:
            return data
        raise IndexError

這里是一個用它來訪問特定平台API的服務器/網關代碼片段。

environ['wsgi.file_wrapper'] = FileWrapper
result = application(environ, start_response)

try:
    if isinstance(result, FileWrapper):
        # check if result.filelike is usable w/platform-specific
        # API, and if so, use that API to transmit the result.
        # If not, fall through to normal iterable handling
        # loop below.

    for data in result:
        # etc.

finally:
    if hasattr(result, 'close'):
        result.close()
Questions and AnswersQA問答
1.為什么evniron必須是字典?用它的子類會有什么問題呢?
用字典的原理是為了最大化滿足在服務器間的移植性。另一種選擇就是定義一些字典的子集,用字典的方法作為標准的便捷的接口。然而事實上,大多服務器可能只需要找到一個字典就足夠了,並且框架的作者會預計完整的字典特性可用,因為多半情況是這樣的。但是如果一些服務器選擇不使用字典,那么將可能會有互用性的問題出現盡管服務器給出了一致性的規范。因此使用強制的字典簡化了規范和互相的驗證確保。
 
注意這些不妨礙服務器或框架開發者向evnrion字典里加入自定義的變量來提供特別的服務。這是提供任何value-added服務推薦的方式。
 
2.為什么你能執行write()並且yield 字符串/返回一個迭代器?我們不應該選擇一種做法嗎?
如果我們僅僅使用迭代的做法,那么現存的框架面臨可用的"push"被惡化。如果我們只支持通過write()推送,那么服務器在傳輸大文件的時候性能將惡化(如果一個工作線程沒有將所有的output都發送完成,那么將無法進行下一個新的request請求)。因此,這種平衡允許應用程序支持兩個方法,視情況而定,比起需要push-only的方式來說,只給服務器實現者一點負擔。
 
3.close()是什么?
當writes在應用程序執行期間完成,應用程序可以通過try/finally塊來保證資源被釋放。但是如果應用程序返回一個迭代,直到迭代器被垃圾收集器收集前任何資源都不會被釋放。約定俗成的close()允許應用程序在結束一個request請求時釋放臨界資源,並且它向前兼容於在PEP 325中被提議的在迭代器中的try/finally.
 
4.為什么這個接口如此低級?我如果想加入一些特性呢(例如cookies, sessions, persistence,...)
這並不是另一個python的web框架,這僅僅是一個框架和web服務器之間通信的方法,反之亦然。如果你想使用這些特性,你需要選擇一個提供這些你想要的特性的框架。並且如果這些框架讓你創建一個WSGI應用程序,你應當能讓它在大部份支持WSGI的服務器上運行。同樣,一些WSGI服務器可能通過在他們提供的evniron字典里的對象來提供一些額外的服務,可以參閱這些服務器適當的文檔了解詳情。(當然,使用這樣擴展的應用程序將無法移植到其他的WSGI-based服務器上)
 
5.為什么使用CGI的變量代替好的老HTTP頭,並且為什么讓他們和WSGI定義的變量混合在一起呢?
許多現存的框架很大程序上是建立在CGI規范的基礎上的,並且現存的web服務器知道如何生成CGI變量。相比之下,一些可選的方式來呈現HTTP信息會是分散破碎的並且缺乏市場。因此使用"CGI標准"看起來是個好的辦法來利用現有的實現。對於將他們同WSGI變量混合在一起,分離他們的話會導致需要兩個字典參數需要傳入,這樣做沒什么好處。
 
6.關於狀態字符串,我們可不可以僅僅使用數字來代替,像用200代替"200 OK"?
這樣做會使服務器或網關被復雜化,因為他們需要一個字符串與數值的映射表。相比之下,應用程序或框架作者在他們輸入一些額外的信息到他們處理細節的響應代碼中要簡單,並且現存的框架時常有一個表包含這些必要的信息。所以,總體來說這個讓應用程序/框架來負責要比服務器或網關來負責要好。
 
7.為什么wsgi.run_once不能保證app只運行一次?
因為它只不過是建議應用程序應當"裝配稀少地運行"。這是用於應用程序框架用不同的模式來操作緩存,sessions等待。在"multiple run"模式下,這樣的框架可能會預先裝載緩存,並且在每個request請求之后不會寫操作,如寫入logs或session數據到硬盤上。在"single run"模式下,這樣的框架避免預加載,避免每個request請求之后flush所有必要的寫操作。
 
然而,為了驗證在后者的模式下應用程序或框架的正確動作,可能會必要地(或是權宜之計)調用它不止一次。因此,一個應用程序應當不能僅僅因為設置了wsgi.run_once為True就假定它肯定不會再運行,
 
8.Feature x(dictionaries, callables, etc.)對於應用程序代碼來說是很丑陋的,我們可以使用對象來代替嗎?
 
WSGI所有的這些選擇實現都是為了從另一個特性中解耦合;重新組合這些特性到一個封裝的對象中會使更難寫服務器/網關,並且會使書寫中間件來代替或修改一部分整體的功能的難度上一個數量級。
本質上,中間件希望有個"責任鏈"模式,憑這個對於一些功能來說它可以看作是一個"handler",而讓其他東西保持不變。這用普通的python對象是非常難的,如果這接口是保持可擴展性的。例如,一個必須使用__getattr__或者__getattribut__的覆蓋,來確保這些擴展(比如一個WSGI未來的版本定義的一個變量)是被通過的。
 
這種類型的代碼是出了名的難以保證100%正確,有些人可能會自己重寫。他們會因此復制其他人的實現,但是從其他已經正確的案例中復制過來的時候卻未能成功更新。
 
進一步講,這樣必要的樣本將純碎excise,一個開發者為了能讓應用程序框架得到更漂亮的API而向中間件開發者支付稅務。但是應用程序框架開發者會通常只升級一個框架用來支持WSGI,並且對於框架整體來說只是非常小的一部分。這可能會成為他們第一次(甚至是最后一次)的WSGI實現,並且因此他們可能實現這些規范起來伸手可及。因此,努力使使用對象屬性創造出來的API漂亮這類的做法,可能會遺失掉這些支持者。
 
我們鼓勵那些希望有漂亮的(或者是改進的)WSGI接口的人,對直接開發web應用程序項目(與web框架開發)的用戶開發為方便應用程序開發者而包裝過WSGI的API或框架,通過這種方式,WSGI可以保持對服務器或中間件的便利性,而不是對應用程序作者"丑陋"。
 
Proposed/Under Discussion  討論

These items are currently being discussed on the Web-SIG and elsewhere, or are on the PEP author's "to-do" list:

  • Should wsgi.input be an iterator instead of a file? This would help for asynchronous applications and chunked-encoding input streams.
  • Optional extensions are being discussed for pausing iteration of an application's output until input is available or until a callback occurs.
  • Add a section about synchronous vs. asynchronous apps and servers, the relevant threading models, and issues/design goals in these areas.

 

完。水平有限,如果有朋友發現文中許多翻譯不當的地方,請隨時指正。多謝。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM