PEP-3333

Python Web 服务器网关接口(原文附译文)

原文地址:PEP-3333

译文项目:

Preface for Readers of PEP 333(致PEP333读者的前言)

This is an updated version of PEP 333, modified slightly to improve usability under Python 3, and to incorporate several long-standing de facto amendments to the WSGI protocol. (Its code samples have also been ported to Python 3.) 本文档是PEP333的升级版,针对Python3进行了可用性方面的细微修改,采纳了几个针对WSGI协议的存在已久的事实修订(相关代码示例也已经移植到Python3)。

While for procedural reasons, this must be a distinct PEP, no changes were made that invalidate previously-compliant servers or applications under Python 2.x. If your 2.x application or server is compliant to PEP 333, it is also compliant with this PEP. 由于流程上的原因,本次修改是独立的,不会影响到Python2.x下服务端或应用端的程序。如果你的程序遵守PEP333,则它必然符合此次升级后的要求。

Under Python 3, however, your app or server must also follow the rules outlined in the sections below titled, A Note On String Types, and Unicode Issues. 但是如果你的程序运行在Python3环境下,则必须注意下文中「字符串类型」和「Unicode编码」两节中的要求。

For detailed, line-by-line diffs between this document and PEP 333, you may view its SVN revision history, from revision 84854 forward. 文本上的具体差别可以查看SVN历史版本,版本号84854之前的是老版。

Abstract(摘要)

This document specifies a proposed standard interface between web servers and Python web applications or frameworks, to promote web application portability across a variety of web servers. 本文档描述了Web服务器与Web应用或框架的标准交互接口,以提高PythonWeb应用在不同Web服务器之间具有可移植性。

Original Rationale and Goals(from PEP 333)(原理和目标 (来自 PEP 333))

Python currently boasts a wide variety of web application frameworks, such as Zope, Quixote, Webware, SkunkWeb, PSO, and Twisted Web -- to name just a few. This wide variety of choices can be a problem for new Python users, because generally speaking, their choice of web framework will limit their choice of usable web servers, and vice versa. Python目前有很多Web框架,比如Zope,Quixote,Webware,SkunkWeb,PSO和Twisted等等。新手面对如此多的选择十分纠结,因为一般来说,框架的选择会限制他们对Web服务器的选择,反之亦然。

By contrast, although Java has just as many web application frameworks available, Java's servlet API makes it possible for applications written with any Java web application framework to run in any web server that supports the servlet API. 相比之下,Java也有非常多的Web框架,但是它的「servlet」API使得用户无论选择哪种框架,都可以保证程序正常运行在支持该API的任何Web服务器上。

The availability and widespread use of such an API in web servers for Python -- whether those servers are written in Python (e.g. Medusa), embed Python (e.g. mod_python), or invoke Python via a gateway protocol (e.g. CGI, FastCGI, etc.) -- would separate choice of framework from choice of web server, freeing users to choose a pairing that suits them, while freeing framework and server developers to focus on their preferred area of specialization. 无论Web服务器是由Python写成,还是内嵌Python,抑或使用CGI和FastCGI之类的网关协议调用Python,在Python世界推广类似的API将使Web框架的选择与Web服务器无关,使框架和服务端的开发者能够专注于他们各自的领域。

This PEP, therefore, proposes a simple and universal interface between web servers and web applications or frameworks: the Python Web Server Gateway Interface (WSGI). 因此本文档提出了一种简单且通用的接口以完成Web服务器与应用和框架的交互:PythonWeb服务器网关接口(WSGI)。

But the mere existence of a WSGI spec does nothing to address the existing state of servers and frameworks for Python web applications. Server and framework authors and maintainers must actually implement WSGI for there to be any effect. 不过仅仅一个WSGI的存在并不足以缓解当前PythonWeb开发中的困境,只有在服务端和框架中真的实现了WSGI才行。

However, since no existing servers or frameworks support WSGI, there is little immediate reward for an author who implements WSGI support. Thus, WSGI must be easy to implement, so that an author's initial investment in the interface can be reasonably low. 由于现有的服务端和框架并不支持WSGI,必须给哪些愿意支持WSGI的开发者们一点好处,故WSGI必须易于实现,以保证实现它的代价比较小。

Thus, simplicity of implementation on both the server and framework sides of the interface is absolutely critical to the utility of the WSGI interface, and is therefore the principal criterion for any design decisions. 因此,在服务端和框架中都容易实现对于WSGI的实用性至关重要,也是所有设计决策的主要考量。

Note, however, that simplicity of implementation for a framework author is not the same thing as ease of use for a web application author. WSGI presents an absolutely no frills interface to the framework author, because bells and whistles like response objects and cookie handling would just get in the way of existing frameworks' handling of these issues. Again, the goal of WSGI is to facilitate easy interconnection of existing servers and applications or frameworks, not to create a new web framework. 在Web框架中易于实现并不代表对Web应用开发者也易用。WSGI向Web框架开发者提供了绝对简洁的接口,因为类似响应对象(responseobjects)和cookie管理这样锦上添花的功能只会妨碍现有框架本身对应的功能。重申一遍,WSGI的目标是使现有Web服务器与Web应用或框架的交互变得更容易,而不是发明一个新的Web框架。

Note also that this goal precludes WSGI from requiring anything that is not already available in deployed versions of Python. Therefore, new standard library modules are not proposed or required by this specification, and nothing in WSGI requires a Python version greater than 2.2.2. (It would be a good idea, however, for future versions of Python to include support for this interface in web servers provided by the standard library.) 这个目标要求WSGI不能依赖于任何在当前已部署的Python环境中不支持的功能。所以本次升级并没有产生新的标准库,且只要求用户的Python环境版本不低于2.2.2(不过在未来的Web服务器标准库中集成新标准应该是一个不错的选择)。

In addition to ease of implementation for existing and future frameworks and servers, it should also be easy to create request preprocessors, response postprocessors, and other WSGI-based middleware components that look like an application to their containing server, while acting as a server for their contained applications. 除了在现有和未来出现的框架和服务端易于集成之外,WSGI还应该能很容易地创建请求预处理器、响应处理程序和其他基于WSGI的中间件组件(对于Web服务器它们是应用,但对于它们包含的应用来说则是服务器)。

If middleware can be both simple and robust, and WSGI is widely available in servers and frameworks, it allows for the possibility of an entirely new kind of Python web application framework: one consisting of loosely-coupled WSGI middleware components. Indeed, existing framework authors may even choose to refactor their frameworks' existing services to be provided in this way, becoming more like libraries used with WSGI, and less like monolithic frameworks. This would then allow application developers to choose best-of-breed components for specific functionality, rather than having to commit to all the pros and cons of a single framework. 如果中间件能够既简洁又健壮,WSGI又在服务端和框架中广泛应用,这将使一种全新的PythonWeb框架得以出现:一种由各种松耦合的WSGI中间件组成的框架。现有的框架开发者们甚至会重构他们的现有服务,使他们的框架更像一些和WSGI配合的库而不是一个独立的框架。这样Web应用开发者就可以针对特定的需求选择最好的组件,而不是只能接受某一个框架的所有优点和缺点。

Of course, as of this writing, that day is doubtless quite far off. In the meantime, it is a sufficient short-term goal for WSGI to enable the use of any framework with any server. 当然,前途是光明的,道路是曲折的,WSGI的短期目标是先让任意框架可以与任意Web服务器交互。

Finally, it should be mentioned that the current version of WSGI does not prescribe any particular mechanism for deploying an application for use with a web server or server gateway. At the present time, this is necessarily implementation-defined by the server or gateway. After a sufficient number of servers and frameworks have implemented WSGI to provide field experience with varying deployment requirements, it may make sense to create another PEP, describing a deployment standard for WSGI servers and application frameworks. 最后需要注意的是,当前版本的WSGI并没有规定一个应用具体以何种方式部署在Web服务器或网关服务器上,目前这由二者的具体实现决定。如果足够多实现了WSGI的服务器或网关在实践中产生了这个需求,也许可以另写一份PEP来描述WSGI服务器和应用框架的部署标准。

Specification Overview(规范概述)

The WSGI interface has two sides: the server or gateway side, and the application or framework side. The server side invokes a callable object that is provided by the application side. The specifics of how that object is provided are up to the server or gateway. It is assumed that some servers or gateways will require an application's deployer to write a short script to create an instance of the server or gateway, and supply it with the application object. Other servers and gateways may use configuration files or other mechanisms to specify where an application object should be imported from, or otherwise obtained. WSGI接口有两种形式:服务端和应用端。服务端请求一个由应用端提供的可调用的对象,至于该对象应当如何被提供取决于服务端。有些服务端需要应用程序的部署人员编写一个简短的脚本来启动一个Web服务器或网关服务器的实例,以为此实例提供所需对象;而另一些服务端则需要配置文件或其他机制来指定从哪里导入或者得到所需对象。

In addition to pure servers/gateways and applications/frameworks, it is also possible to create middleware components that implement both sides of this specification. Such components act as an application to their containing server, and as a server to a contained application, and can be used to provide extended APIs, content transformation, navigation, and other useful functions. 除了Web服务器/网关服务器和Web应用/开发框架,还可以创建包含两种接口的中间件组件:对于Web服务器它们是应用,而对于应用来说他们是服务器。中间件可以用来提供扩展API,内容转换,导航和其他有用的功能。

Throughout this specification, we will use the term a callable to mean a function, method, class, or an instance with a __call__ method. It is up to the server, gateway, or application implementing the callable to choose the appropriate implementation technique for their needs. Conversely, a server, gateway, or application that is invoking a callable must not have any dependency on what kind of callable was provided to it. Callables are only to be called, not introspected upon. 在本文档中,我们使用术语「可调用者」代表「一个函数,方法,类,或者拥有__call__方法的一个实例」。实现「可调用者」的Web服务器,网关服务器或应用程序可以根据需要选择合适的实现方式;相反,请求「可调用者」的Web服务器,网关服务器或应用程序不可以依赖具体的实现方式。「可调用者」只能被调用,不能自省。

A Note on String Types(一条关于字符串类型的笔记)

In general, HTTP deals with bytes, which means that this specification is mostly about handling bytes. 一般来说,HTTP协议处理字节流,也就是说本文档主要面向字节流的处理。

However, the content of those bytes often has some kind of textual interpretation, and in Python, strings are the most convenient way to handle text. 不过字节流经常是文本意义上可读的,而在Python中,字符串类型是处理文本的趁手工具。

But in many Python versions and implementations, strings are Unicode, rather than bytes. This requires a careful balance between a usable API and correct translations between bytes and text in the context of HTTP... especially to support porting code between Python implementations with different str types. 但是在很多Python的版本和实现中,字符串是Unicode编码的,而不是字节流。这要求我们在HTTP字节流与文本的相互转换和好用的API之间保持很好的平衡,尤其要注意支持基于不同版本的Python程序之间的可移植性,这些版本中字符串类型不尽相同。

WSGI therefore defines two kinds of string: 因此WSGI定义了两种字符串类型:

  • Native strings (which are always implemented using the type named str) that are used for request/response headers and metadata 原生字符串(一般使用str类型实现)。这种字符串用在请求和响应的包头和元数据中。

  • Bytestrings (which are implemented using the bytes type in Python 3, and str elsewhere), that are used for the bodies of requests and responses (e.g. POST/PUT input data and HTML page outputs). 字节流字符串(在Python3中使用bytes类型实现,其他版本中使用str类型实现)。这种字符串用在请求和响应的包内容中(比如POST方法或PUT方法的输入数据以及HTML页面的输出)。

Do not be confused however: even if Python's str type is actually Unicode under the hood, the content of native strings must still be translatable to bytes via the Latin-1 encoding! (See the section on Unicode Issues later in this document for more details.) 大家一定要注意不要搞混了:即使Python的str类型实质上是Unicode编码的,但是原生字符串的内容仍然将通过Latin-1编码转换为字节流(参见下文「Unicode编码」一节可获得更多信息)。

In short: where you see the word string in this document, it refers to a native string, i.e., an object of type str, whether it is internally implemented as bytes or unicode. Where you see references to bytestring, this should be read as an object of type bytes under Python 3, or type str under Python 2. 简而言之,本文档中的「字符串」这个词都是指「原生字符串」,亦即一个str类型的对象,无论其实质上是字节流还是Unicode编码。任何地方出现的「字节流字符串」,都是指Python3下bytes类型的一个实例,或者Python2下str类型的一个实例。

And so, even though HTTP is in some sense really just bytes, there are many API conveniences to be had by using whatever Python's default str type is. 因此,虽然HTTP某种意义上来说就是字节流,使用Python默认的字符串类型来解析会带来不少API使用上的好处。

The Application/Framework Side(应用/框架端)

The application object is simply a callable object that accepts two arguments. The term object should not be misconstrued as requiring an actual object instance: a function, method, class, or instance with a __call__ method are all acceptable for use as an application object. Application objects must be able to be invoked more than once, as virtually all servers/gateways (other than CGI) will make such repeated requests. 应用端接口应用对象是一个简单的接受两个参数的可调用对象。这里的对象并不是真的需要一个对象实例,一个函数、方法、类、或者带有__call__方法的对象实例都可以当作应用对象。应用对象必须可以多次被调用,因为实际上所有的服务端(而非CGI)都会产生这样的重复请求。

(Note: although we refer to it as an application object, this should not be construed to mean that application developers will use WSGI as a web programming API! It is assumed that application developers will continue to use existing, high-level framework services to develop their applications. WSGI is a tool for framework and server developers, and is not intended to directly support application developers.) (注意:虽然我们称之为「应用」对象,但这并不是说程序员可以把WSGI当做Web编程API来调用!我们假定应用开发者仍然使用更高层面上的框架服务来开发应用,WSGI是提供给框架和Web服务器开发者使用的工具,并不打算直接对应用开发者提供支持。)

Here are two example application objects; one is a function, and the other is a class: 这里有两个应用对象的示例,一个是函数,另一个是类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
HELLO_WORLD = b"Hello world!\n"

def simple_app(environ, start_response):
"""Simplest possible application object"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return [HELLO_WORLD]

class AppClass:
"""Produce the same output, but using a class

(Note: 'AppClass' is the "application" here, so calling it
returns an instance of 'AppClass', which is then the iterable
return value of the "application callable" as required by
the spec.

If we wanted to use *instances* of 'AppClass' as application
objects instead, we would have to implement a '__call__'
method, which would be invoked to execute the application,
and we would need to create an instance for use by the
server or gateway.
"""

def __init__(self, environ, start_response):
self.environ = environ
self.start = start_response

def __iter__(self):
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
self.start(status, response_headers)
yield HELLO_WORLD

The Server/Gateway Side(服务器/网关端)

The server or gateway invokes the application callable once for each request it receives from an HTTP client, that is directed at the application. To illustrate, here is a simple CGI gateway, implemented as a function taking an application object. Note that this simple example has limited error handling, because by default an uncaught exception will be dumped to sys.stderr and logged by the web server. 服务端接口服务端为HTTP客户端发来的每一个请求调用一次可调用者,这是由应用决定的(?)。为了方便说明,这里以一个获取应用对象的函数实现了一个简单的CGI网关。请注意,这个例子的错误处理功能很有限,因为默认情况下没有被捕获的异常都会被输出到sys.stderr并被Web服务器记录下来。:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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):
environ = {k: unicode_to_wsgi(v) for k,v in os.environ.items()}
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):
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

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: Components that Play Both Sides(中间件: 两边都起作用的元素)

Note that a single object may play the role of a server with respect to some application(s), while also acting as an application with respect to some server(s). Such middleware components can perform such functions as: 分饰两角的组件同一个对象既可以作为服务端存在,也可以作为应用端存在。这样的中间件可以完成以下功能:

  • Routing a request to different application objects based on the target URL, after rewriting the environ accordingly. 重写上文代码中的environ之后,可以根据目标URL将请求转发到不同的应用程序对象

  • Allowing multiple applications or frameworks to run side-by-side in the same process 允许多个应用程序或框架在一个进程中同时运行

  • Load balancing and remote processing, by forwarding requests and responses over a network 通过转发请求和响应,实现负载均衡和远程处理

  • Perform content postprocessing, such as applying XSL stylesheets 对内容进行后期处理,比如引入XSL样式表

The presence of middleware in general is transparent to both the server/gateway and the application/framework sides of the interface, and should require no special support. A user who desires to incorporate middleware into an application simply provides the middleware component to the server, as if it were an application, and configures the middleware component to invoke the application, as if the middleware component were a server. Of course, the application that the middleware wraps may in fact be another middleware component wrapping another application, and so on, creating what is referred to as a middleware stack. 中间件的存在对于服务端和应用端来说都应该是透明的,并且不需要特殊的支持。希望在应用程序中加入中间件的用户只需简单的把中间件当作应用提供给Web服务器,并配置中间件使其以服务器的身份与应用程序交互即可。当然,中间件包装后提供给服务器的应用也可以是另一个中间件,如此连锁下去便构成了所谓的「中间件栈」。

For the most part, middleware must conform to the restrictions and requirements of both the server and application sides of WSGI. In some cases, however, requirements for middleware are more stringent than for a pure server or application, and these points will be noted in the specification. 一般情况下,中间件要符合WSGI对应用端和服务端提出的一些限制和要求,有些时候这样的限制甚至比纯粹的服务端或应用端还要严格,这些地方我们会特别指出。

Here is a (tongue-in-cheek) example of a middleware component that converts text/plain responses to pig Latin, using Joe Strout's piglatin.py. (Note: a real middleware component would probably use a more robust way of checking the content type, and should also check for a content encoding. Also, this simple example ignores the possibility that a word might be split across a block boundary.) 下面是一个非正式的中间件组件的示例,使用JoeStrout的piglatin.py将text/plain的响应转换成儿童黑话(注意:真正的中间件应该使用更加安全的方式——应该检查内容的类型和编码,而且这个简单的例子还忽略了一个单词可能会被拆分到两个包中的可能性)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from piglatin import piglatin

class LatinIter:

"""Transform iterated output to piglatin, if it's okay to do so

Note that the "okayness" can change until the application yields
its first non-empty bytestring, so 'transform_ok' has to be a mutable
truth value.
"""

def __init__(self, result, transform_ok):
if hasattr(result, 'close'):
self.close = result.close
self._next = iter(result).__next__
self.transform_ok = transform_ok

def __iter__(self):
return self

def __next__(self):
if self.transform_ok:
return piglatin(self._next()) # call must be byte-safe on Py3
else:
return self._next()

class Latinator:

# by default, don't transform output
transform = False

def __init__(self, application):
self.application = application

def __call__(self, environ, start_response):

transform_ok = []

def start_latin(status, response_headers, exc_info=None):

# Reset ok flag, in case this is a repeat call
del transform_ok[:]

for name, value in response_headers:
if name.lower() == 'content-type' and value == 'text/plain':
transform_ok.append(True)
# Strip content-length if present, else it'll be wrong
response_headers = [(name, value)
for name, value in response_headers
if name.lower() != 'content-length'
]
break

write = start_response(status, response_headers, exc_info)

if transform_ok:
def write_latin(data):
write(piglatin(data)) # call must be byte-safe on Py3
return write_latin
else:
return write

return LatinIter(self.application(environ, start_latin), transform_ok)


# Run foo_app under a Latinator's control, using the example CGI gateway
from foo_app import foo_app
run_with_cgi(Latinator(foo_app))

Specification Deetails(规范细节)

The application object must accept two positional arguments. For the sake of illustration, we have named them environ and start_response, but they are not required to have these names. A server or gateway must invoke the application object using positional (not keyword) arguments. (E.g. by calling result = application(environ, start_response) as shown above.) 应用对象必须接受两个形式参数,为了方便说明我们不妨分别命名为environ和start_response,但这并不是强制的。服务端必须使用形式参数(而非关键字)调用应用对象(就像这样:result=application(environ,start_response))。

The environ parameter is a dictionary object, containing CGI-style environment variables. This object must be a builtin Python dictionary (not a subclass, UserDict or other dictionary emulation), and the application is allowed to modify the dictionary in any way it desires. The dictionary must also include certain WSGI-required variables (described in a later section), and may also include server-specific extension variables, named according to a convention that will be described below. environ参数是个字典对象,包含CGI风格的环境变量。这个对象必须是一个Python内建的字典对象(不能是子类、UserDict或其他对字典对象的模仿),应用程序可以以任意方式修改这个字典。environ还应该包含一些特定的WSGI所需的变量(在后面的小节里会详述),也可以包含一些服务器特定的扩展变量,通过下文的约定命名。

The start_response parameter is a callable accepting two required positional arguments, and one optional argument. For the sake of illustration, we have named these arguments status, response_headers, and exc_info, but they are not required to have these names, and the application must invoke the start_response callable using positional arguments (e.g. start_response(status, response_headers)). start_response参数是一个接受两个固定参数和一个可选参数的可调用者。为了方便说明,我们分别命名为status、response_headers和exc_info,同样的,这样命名也不是强制的。应用程序必须用形式参数来调用start_response(就像这样:start_response(status,response_headers))。

The status parameter is a status string of the form 999 Message here, and response_headers is a list of (header_name, header_value) tuples describing the HTTP response header. The optional exc_info parameter is described below in the sections on The start_response() Callable and Error Handling. It is used only when the application has trapped an error and is attempting to display an error message to the browser. status参数是一个形式上像「999Messagehere」这样的状态字符串。而response_headers参数是一个(header_name,header_value)元组的列表,用来描述HTTP响应头。可选的exc_info参数会在下面的「start_response()函数」和「错误处理」两节中详述,它只有在应用程序产生了错误并希望在浏览器上显示相关信息的时候才有用。

The start_response callable must return a write(body_data) callable that takes one positional parameter: a bytestring to be written as part of the HTTP response body. (Note: the write() callable is provided only to support certain existing frameworks' imperative output APIs; it should not be used by new applications or frameworks if it can be avoided. See the Buffering and Streaming section for more details.) start_response可调用者必须返回一个write(body_data)可调用者,它接受一个形式参数:一个将要被作为HTTP响应体的一部分输出的字节流字符串(注意:提供可调用者write()只是为了支持现有框架的必要的输出API,新的应用程序或框架尽量避免使用,详细情况参见缓冲区和流处理一节)。

When called by the server, the application object must return an iterable yielding zero or more bytestrings. This can be accomplished in a variety of ways, such as by returning a list of bytestrings, or by the application being a generator function that yields bytestrings, or by the application being a class whose instances are iterable. Regardless of how it is accomplished, the application object must always return an iterable yielding zero or more bytestrings. 当被服务器调用的时候,应用对象必须返回一个生成0或多个字节流字符串的迭代器,这可以通过多种方法实现,比如返回一个字节流字符串的列表,或者应用程序本身是一个字节流字符串的生成器,或者应用程序是一个类而其实例是可迭代的.不管怎么实现,应用对象必须总是返回一个生成0或多个字节流字符串的迭代器。

The server or gateway must transmit the yielded bytestrings to the client in an unbuffered fashion, completing the transmission of each bytestring before requesting another one. (In other words, applications should perform their own buffering. See the Buffering and Streaming section below for more on how application output must be handled.) 服务器端必须将生成的字节流字符串以一种无缓冲的方式传送到客户端,每次传完一个再去获取下一个(换句话说,应用程序应该自己实现缓冲,更多关于应用程序如何处理输出的细节参见「缓冲区和流处理」小节)。

The server or gateway should treat the yielded bytestrings as binary byte sequences: in particular, it should ensure that line endings are not altered. The application is responsible for ensuring that the bytestring(s) to be written are in a format suitable for the client. (The server or gateway may apply HTTP transfer encodings, or perform other transformations for the purpose of implementing HTTP features such as byte-range transmission. See Other HTTP Features, below, for more details.) 服务端应该把产生的字节流字符串当作字节流对待:尤其必须保证没修改行尾。应用程序负责确保字符串以与客户端匹配的编码输出(服务端可能会附加HTTP传送编码,或者为了实现一些HTTP特性而进行一些转换,比如字节范围转换(?),更多细节参见「其他HTTP特性」小节)

If a call to len(iterable) succeeds, the server must be able to rely on the result being accurate. That is, if the iterable returned by the application provides a working __len__() method, it must return an accurate result. (See the Handling the Content-Length Header section for information on how this would normally be used.) 如果调用len(iterable)成功,服务器将认为结果是正确的。也就是说,应用程序返回的可迭代的字符串提供了一个靠谱的__len__()方法,那么肯定返回了正确的结果(关于这个方法正常情况下如何被使用参见「处理Content-Length头」)。

If the iterable returned by the application has a close() method, the server or gateway must call that method upon completion of the current request, whether the request was completed normally, or terminated early due to an application error during iteration or an early disconnect of the browser. (The close() method requirement is to support resource release by the application. This protocol is intended to complement PEP 342's generator support, and other common iterables with close() methods.) 如果应用程序返回的迭代器有close()方法,则不管该请求是正常结束还是由于迭代错误或浏览器失去连接而终止,服务端都必须在结束该请求之前调用这个方法(这是用来支持应用程序对资源的释放,以完善PEP342的生成器支持和其他含有close()方法的一般迭代器)。

Applications returning a generator or other custom iterator should not assume the entire iterator will be consumed, as it may be closed early by the server. 应用程序返回的生成器或其他定制的迭代器并不一定会被使用,可能直接就被服务端关闭了。

(Note: the application must invoke the start_response() callable before the iterable yields its first body bytestring, so that the server can send the headers before any body content. However, this invocation may be performed by the iterable's first iteration, so servers must not assume that start_response() has been called before they begin iterating over the iterable.) (注意:应用程序必须在迭代器生成第一个字节流字符串之前调用start_response()可调用者,这样服务器才能在发送任何主体内容之前发送响应头。不过这一步也可以在迭代器第一次迭代的时候进行,所以服务器不能假定开始迭代之前start_response()已经被调用过了。)

Finally, servers and gateways must not directly use any other attributes of the iterable returned by the application, unless it is an instance of a type specific to that server or gateway, such as a file wrapper returned by wsgi.file_wrapper (see Optional Platform-Specific File Handling). In the general case, only attributes specified here, or accessed via e.g. the PEP 234 iteration APIs are acceptable. 除以上提及的之外,服务端不能直接使用任何应用程序返回的迭代器属性,除非该属性是针对服务端特定类型的一个实例,比如wsgi.file_wrapper返回的「文件包装器」(参见「(可选)特定平台上的文件处理」)。通常情况下,只有这里指定的属性,或者通过如PEP234迭代API之类的途径获取的其他属性才是可以访问的。

environ Variables(环境变量)

The environ dictionary is required to contain these CGI environment variables, as defined by the Common Gateway Interface specification. The following variables must be present, unless their value would be an empty string, in which case they may be omitted, except as otherwise noted below. environ字典中要求包含下面这些在CGI规范中定义了的CGI环境变量。除非其值是空字符串(这种情况下如果下面没有特别指出的话它们可能会被忽略),下面这些变量必须存在:

REQUEST_METHOD
The HTTP request method, such as GET or POST. This cannot ever be an empty string, and so is always required. HTTP请求的类型,比如「GET」或者「POST」。这个不可能是空字符串,所以是必须给出的。

SCRIPT_NAME
The initial portion of the request URL's path that corresponds to the application object, so that the application knows its virtual location. This may be an empty string, if the application corresponds to the root of the server. URL请求中路径的开始部分,对应应用程序对象(?),这样应用程序就知道它的虚拟位置。如果该应用程序对应服务器的根目录的话,它可能是空字符串。

PATH_INFO
The remainder of the request URL's path, designating the virtual location of the request's target within the application. This may be an empty string, if the request URL targets the application root and does not have a trailing slash. URL请求中路径的剩余部分,指定请求的目标在应用程序内部的虚拟位置(?)。如果请求的目标是应用程序根目录并且没有末尾的斜杠的话,可能为空字符串。

QUERY_STRING
The portion of the request URL that follows the ?, if any. May be empty or absent. URL请求中跟在「?」后面的那部分,可能为空或不存在。

CONTENT_TYPE
The contents of any Content-Type fields in the HTTP request. May be empty or absent. HTTP请求中任何Content-Type域的内容,可能为空或不存在。

CONTENT_LENGTH
The contents of any Content-Length fields in the HTTP request. May be empty or absent. HTTP请求中任何Content-Length域的内容,可能为空或不存在。

SERVER_NAME, SERVER_PORT
When combined with SCRIPT_NAME and PATH_INFO, these two strings can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to SERVER_NAME for reconstructing the request URL. See the URL Reconstruction section below for more detail. SERVER_NAME and SERVER_PORT can never be empty strings, and so are always required. 这些变量可以和SCRIPT_NAME、PATH_INFO一起组成完整的URL。然而要注意的是,重建请求URL的时候应该优先使用HTTP_HOST而非SERVER_NAME。详细内容参见「URL重建」。SERVER_NAME和SERVER_PORT永远不能为空字符串,也总是必须存在的。

SERVER_PROTOCOL
The version of the protocol the client used to send the request. Typically this will be something like HTTP/1.0 or HTTP/1.1 and may be used by the application to determine how to treat any HTTP request headers. (This variable should probably be called REQUEST_PROTOCOL, since it denotes the protocol used in the request, and is not necessarily the protocol that will be used in the server's response. However, for compatibility with CGI we have to keep the existing name.) 客户端发送请求所使用协议的版本。通常是类似「HTTP/1.0」或「HTTP/1.1」的东西,可以被用来判断如何处理请求包头。(既然这个变量表示的是请求中使用的协议,而且和服务器响应时使用的协议无关,也许它应该被叫做REQUEST_PROTOCOL。不过为了保持和CGI的兼容性,我们还是使用这个名字。)

HTTP_ Variables
Variables corresponding to the client-supplied HTTP request headers (i.e., variables whose names begin with HTTP_). The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request. 对应客户端提供的HTTP请求包头(即名字以「HTTP_」开头的各种变量)。这些变量的存在与否应该与请求中对应的HTTP包头是否存在相一致。

A server or gateway should attempt to provide as many other CGI variables as are applicable. In addition, if SSL is in use, the server or gateway should also provide as many of the Apache SSL environment variables as are applicable, such as HTTPS=on and SSL_PROTOCOL. Note, however, that an application that uses any CGI variables other than the ones listed above are necessarily non-portable to web servers that do not support the relevant extensions. (For example, web servers that do not publish files will not be able to provide a meaningful DOCUMENT_ROOT or PATH_TRANSLATED.) 服务端应该尽可能提供所有可用的CGI变量。另外,如果使用了SSL,服务端也应该尽可能提供所有可用的ApacheSSL环境变量,比如HTTPS=on和SSL_PROTOCOL。不过要注意,使用了任何上面没有列出的CGI变量的应用程序对不支持相关扩展的服务器来说就不具有可移植性了。(比如,不发布文件的Web服务器就不能提供有意义的DOCUMENT_ROOT变量或PATH_TRANSLATED变量。)

A WSGI-compliant server or gateway should document what variables it provides, along with their definitions as appropriate. Applications should check for the presence of any variables they require, and have a fallback plan in the event such a variable is absent. 支持WSGI的服务端应该在自述文档中说明它们可以提供些什么变量;应用端则应该简称所有需要的变量的存在性,并且在某变量不存在的时候有备用方案。

Note: missing variables (such as REMOTE_USER when no authentication has occurred) should be left out of the environ dictionary. Also note that CGI-defined variables must be native strings, if they are present at all. It is a violation of this specification for any CGI variable's value to be of any type other than str. 注意:不存在的变量(比如在不需要验证的情况下的REMOTE_USER变量)应该被移出environ字典。另外要注意CGI定义的变量如果存在的话必须是原生字符串。任何str类型以外的CGI变量都是不符合本规范。

In addition to the CGI-defined variables, the environ dictionary may also contain arbitrary operating-system environment variables, and must contain the following WSGI-defined variables: 除了CGI定义的变量,environ字典也可以包含任意操作系统的环境变量,并且必须包含下面这些WSGI定义的变量:

VariableValue
wsgi.version
The tuple (1, 0), representing WSGI version 1.0. (1,0)元组,代表WSGI1.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. 字符串,表示应用请求的URL所属的协议,通常为「http」或「https」。

wsgi.input
An input stream (file-like object) from which the HTTP request body bytes 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.) 类文件对象的输入流,用于读取HTTP请求包体的内容。(服务端在应用端请求时开始读取,或者预读客户端请求包体内容缓存在内存或磁盘中,或者视情况而定采用任何其他技术提供此输入流。)

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 as a line ending, and assume that it will be converted to the correct line ending by the server/gateway. 类文件对象的输出流,用于写入错误信息,以集中规范地记录程序产生的或其他相关错误信息。这是一个文本流,即应用应该使用「n」来表示行尾,并假定其会被服务端正确地转换。

(On platforms where the str type is unicode, the error stream should accept and log arbitrary unicode without raising an error; it is allowed, however, to substitute characters that cannot be rendered in the stream's encoding.) (在str类型是Unicode编码的平台上,错误流应该正常接收并记录任意Unicode编码而不报错,并且允许自行替代在该平台编码中无法渲染的字符。)

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. he 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. 很多Web服务器中wsgi.errors是主要的错误日志,也有一些使用sys.stderr或其他形式的文件来记录。Web服务器的自述文档中应该包含如何配置错误日志以及如何找到记录的位置。服务端可以在被要求的情况下,向不同的应用提供不同的错误日志。

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_one
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). 如果服务端期望(但是不保证能得到满足)应用对象在生命周期中之辈调用一次,此变量值为真,否则为假。一般只有在基于类似CGI的网关服务器中此变量才会为真。

Finally, the environ dictionary may also contain server-defined variables. These variables should be named using only lower-case letters, numbers, dots, and underscores, and should be prefixed with a name that is unique to the defining server or gateway. For example, mod_python might define variables with names like mod_python.some_variable. 最后,environ字典也可以包含服务端定义的变量。这些变量的名称应当由小写字母、数字、点和下划线组成,并且应当带有一个所在服务端独有的前缀。比如mod_python可以定义mod_python.some_variable这样的变量。

Input and Error Streams(输入和错误流)

The input and error streams provided by the server must support the following methods: 输入流和错误流服务器提供的输入和错误流必须提供以下方法:

MethodStreamNotes
read(size)input1
readline()input1,2
readlines(hint)input1,3
iter()input
flush()errors4
write(str)errors
writelines(seq)errors

The semantics of each method are as documented in the Python Library Reference, except for these notes as listed in the table above: 每个方法的语义如果上面没有特别指出均和Python标准库介绍中记载的一样:

  1. The server is not required to read past the client's specified Content-Length, and should simulate an end-of-file condition if the application attempts to read past that point. The application should not attempt to read more data than is specified by the CONTENT_LENGTH variable. 不要求Web服务器读取超过客户端指定的Content-Length的内容,并且应该在应用尝试读取越界内容时虚拟出一个文件结束符。应用不应该尝试读取超过Content-Length指定长度的内容。

    A server should allow read() to be called without an argument, and return the remainder of the client's input stream. Web服务器应该允许不使用参数调用read(),并返回客户端输入流剩余的部分;

    A server should return empty bytestrings from any attempt to read from an empty or exhausted input stream. 同时服务器应该对任何尝试读取空的或到文件尾的输入流的行为返回空字符串。

  2. Servers should support the optional size argument to readline(), but as in WSGI 1.0, they are allowed to omit support for it. Web服务器应该支持readline()函数的可选「size」参数,但是在WSGI1.0版本中可以忽略这一点。

    (In WSGI 1.0, the size argument was not supported, on the grounds that it might have been complex to implement, and was not often used in practice... but then the cgi module started using it, and so practical servers had to start supporting it anyway!) (在WSGI1.0中,「size」参数并不要求提供,因为可能很难实现也不常用。但是由于CGI模块开始支持它了,所以生产环境中的Web服务器还是得实现「size」参数。)

  3. Note that the hint argument to readlines() is optional for both caller and implementer. The application is free not to supply it, and the server or gateway is free to ignore it. readlines()函数的「hint」参数对于调用者和实现者来说都是可选的。应用端完全可以忽略它,服务端亦然。

  4. Since the errors stream may not be rewound, servers and gateways are free to forward write operations immediately, without buffering. In this case, the flush() method may be a no-op. Portable applications, however, cannot assume that output is unbuffered or that flush() is a no-op. They must call flush() if they need to ensure that output has in fact been written. (For example, to minimize intermingling of data from multiple processes writing to the same error log.) 由于错误流不能重设读写位置,服务端可以使用无缓冲模式来进行写操作。在这种情况下,flush()函数不做任何操作。但是具有良好可移植性的程序不能假设输出流是无缓冲或flush()函数是误操作的,而应当在需要输出真的被写到存储设备中的时候调用flush()函数。(比如防止多进程写数据造成的混乱这种情况。)

The methods listed in the table above must be supported by all servers conforming to this specification. Applications conforming to this specification must not use any other methods or attributes of the input or errors objects. In particular, applications must not attempt to close these streams, even if they possess close() methods. 上表中列出的方法必须被所有遵守本规范的服务端支持。遵守本规范的应用端则不应该调用任何其他的输入流和输出流相关的方法和属性。尤其要注意的是,应用端即使在这些流提供close()方法的情况下,也不应该尝试关闭它们。

The start_response() Callable(可调用start_response())

The second parameter passed to the application object is a callable of the form start_response(status, response_headers, exc_info=None). (As with all WSGI callables, the arguments must be supplied positionally, not by keyword.) The start_response callable is used to begin the HTTP response, and it must return a write(body_data) callable (see the Buffering and Streaming section, below). start_response()可调用者传给应用程序对象的第二个参数是一个形为start_response(status,response_headers,exc_info=None)的可调用者。(像WSGI的所有其他可调用者一样,这个参数必须使用形式参数提供,而不能以关键字参数提供。)start_response可调用者用于开始HTTP响应,它必须返回一个write(body_data)可调用者(参见缓冲区和流处理)。

The status argument is an HTTP status string like 200 OK or 404 Not Found. That is, it is a string consisting of a Status-Code and a Reason-Phrase, in that order and separated by a single space, with no surrounding whitespace or other characters. (See RFC 2616, Section 6.1.1 for more information.) The string must not contain control characters, and must not be terminated with a carriage return, linefeed, or combination thereof. status参数是一个形如「200OK」或「404NotFound」这样的HTTP状态字符串。换言之是一个由状态编号和具体信息组成的字符串,按顺序并用空格隔开,两头没有其他空格和其他字符。(更多信息参见RFC2616,6.1.1小节。)该字符串禁止包含控制字符,也不允许以回车、换行或二者的组合形式结尾。

The response_headers argument is a list of (header_name, header_value) tuples. It must be a Python list; i.e. type(response_headers) is ListType, and the server may change its contents in any way it desires. Each header_name must be a valid HTTP header field-name (as defined by RFC 2616, Section 4.2), without a trailing colon or other punctuation. response_headers参数是一个(header_name,header_value)元组的列表。它必须是一个Python中的列表,即type(response_headers)的值为ListType,并且Web服务器可以以任何方式改变其内容。每一个header_name必须是一个不含冒号或其他标点符号的合法的HTTPheader字段名(RFC26164.2小节中有详细定义)。

Each header_value must not include any control characters, including carriage returns or linefeeds, either embedded or at the end. (These requirements are to minimize the complexity of any parsing that must be performed by servers, gateways, and intermediate response processors that need to inspect or modify response headers.) 每一个header_value禁止包含任何控制字符(包括回车或换行)。(这些要求是为了将那些必须检查或修改响应头的服务端和中间件所必须执行的解析工作的复杂性降到最低。)

In general, the server or gateway is responsible for ensuring that correct headers are sent to the client: if the application omits a header required by HTTP (or other relevant specifications that are in effect), the server or gateway must add it. For example, the HTTP Date: and Server: headers would normally be supplied by the server or gateway. 一般来说,服务端要保证发送到客户端的包头的正确性:如果应用忽略了HTTP规定的包头(或其他类似的内容)服务端必须自己加上。比如HTTP的「Date:」和「Server:」包头一般都是由服务端提供的。

(A reminder for server/gateway authors: HTTP header names are case-insensitive, so be sure to take that into consideration when examining application-supplied headers!) (服务端开发者小贴士:HTTP包头名称是大小写敏感的,所以请确保你们检查应用提供的包头时考虑了这一点!)

Applications and middleware are forbidden from using HTTP/1.1 hop-by-hop features or headers, any equivalent features in HTTP/1.0, or any headers that would affect the persistence of the client's connection to the web server. These features are the exclusive province of the actual web server, and a server or gateway should consider it a fatal error for an application to attempt sending them, and raise an error if they are supplied to start_response(). (For more specifics on hop-by-hop features and headers, please see the Other HTTP Features section below.) 应用和中间件禁止使用HTTP/1.1中的「逐跳」机制和包头,HTTP/1.0中类似的机制也禁用,任何应用客户端到Web服务器的连接持久性的包头也都不允许使用。使用这些特性是服务端的特权,服务端发现客户端违反此规定时应视为致命错误,需在请求提交到start_response()时报错。(关于「逐跳」特性和包头,请参见「其他HTTP特性」小节。)

Servers should check for errors in the headers at the time start_response is called, so that an error can be raised while the application is still running. 在start_response被调用时Web服务器需要检查是否有错误,所以可以在应用正在运行的时候报错。

However, the start_response callable must not actually transmit the response headers. Instead, it must store them for the server or gateway to transmit only after the first iteration of the application return value that yields a non-empty bytestring, or upon the application's first invocation of the write() callable. In other words, response headers must not be sent until there is actual body data available, or until the application's returned iterable is exhausted. (The only possible exception to this rule is if the response headers explicitly include a Content-Length of zero.) 但是,start_response可调用者禁止传送响应包头。只能在服务端缓存起来,当且仅当应用的第一次迭代完成并返回一个非空字节流字符串或应用第一次调用write()可调用者的时候才能由服务端传送。换言之,响应包头只有在包体数据已经准备好,或者应用返回的迭代器已经迭代完成的时候才能被传送出去。(唯一的例外是响应包头显式包含了一个值为零的Content-Length字段。)

This delaying of response header transmission is to ensure that buffered and asynchronous applications can replace their originally intended output with error output, up until the last possible moment. For example, the application may need to change the response status from 200 OK to 500 Internal Error, if an error occurs while the body is being generated within an application buffer. 响应包头的延迟传送是为了保证带缓冲区和异步的应用能够将它们原生的输出替换为错误流,一直到所能允许的最后一刻。举例来说,当应用使用缓冲区生成包体的时候如果出错,应用可能需要将响应状态从「200OK」改为「500InternalError

The exc_info argument, if supplied, must be a Python sys.exc_info() tuple. This argument should be supplied by the application only if start_response is being called by an error handler. If exc_info is supplied, and no HTTP headers have been output yet, start_response should replace the currently-stored HTTP response headers with the newly-supplied ones, thus allowing the application to change its mind about the output when an error has occurred. 如果提供了exc_info参数,则其必须为Python中的sys.exc_info()元组。只有当start_response被错误处理程序调用时,这个参数才应当被提供。如果提供了exc_info参数且没有尚未有任何HTTP包头输出,start_response应该将当前缓存的HTTP响应包头替换成新生成的,从而允许应用在错误发生的时候修改输出。

However, if exc_info is provided, and the HTTP headers have already been sent, start_response must raise an error, and should re-raise using the exc_info tuple. That is: 但是,如果HTTP包头在其时已有输出,start_response必须报错,且应当使用exc_info元组再报一次::

1
raise exc_info[1].with_traceback(exc_info[2])
This will re-raise the exception trapped by the application, and in principle should abort the application. (It is not safe for the application to attempt error output to the browser once the HTTP headers have already been sent.) The application must not trap any exceptions raised by start_response, if it called start_response with exc_info. Instead, it should allow such exceptions to propagate back to the server or gateway. See Error Handling below, for more details. 以上代码会把应用捕获的异常再抛出一次,原则上会终止应用。(当HTTP包头已经被送出后应用尝试将错误信息输出至浏览器的行为是不安全的。)如果应用使用exc_info参数调用start_response,则禁止捕获任何由start_response抛出的异常,而应该让该异常被返回到服务端。详见「错误处理」小节。

The application may call start_response more than once, if and only if the exc_info argument is provided. More precisely, it is a fatal error to call start_response without the exc_info argument if start_response has already been called within the current invocation of the application. This includes the case where the first call to start_response raised an error. (See the example CGI gateway above for an illustration of the correct logic.) 当且仅当提供exc_info参数时,应用可能会调用start_response多次。说得更精确一点,如果start_response应该在当前的应用调用中被调用过了,再次调用时如果不提供exc_info参数就会引发一个致命错误。第一次调用start_response出错也包括在这种情况中。(参见上文的CGI网关示例以领会正确的逻辑流程。)

Note: servers, gateways, or middleware implementing start_response should ensure that no reference is held to the exc_info parameter beyond the duration of the function's execution, to avoid creating a circular reference through the traceback and frames involved. The simplest way to do this is something like: 注意,集成了start_response的Web服务器、网关服务器和中间件必须保证在start_response执行期之外的时间内不能访问到exc_info,以避免在追踪和涉及到框架时发生循环引用。最简单的处理方式如下

1
2
3
4
5
6
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.
The example CGI gateway provides another illustration of this technique. CGI网关样例程序则提供了另一种处理方法。

Handling the Content-Length Header(处理Content-Length头)

If the application supplies a Content-Length header, the server should not transmit more bytes to the client than the header allows, and should stop iterating over the response when enough data has been sent, or raise an error if the application tries to write() past that point. (Of course, if the application does not provide enough data to meet its stated Content-Length, the server should close the connection and log or otherwise report the error.) 处理Content-Length头如果应用提供Content-Length包头,Web服务器不应该传送大于该包头指定长度的数据给客户端,而应该在发送了足量数据之后停止对改请求进行迭代,或者在应用尝试在此后调用write()时报错。(当然,如果应用提供的数据量不够Content-Length指定的大小,Web服务器应当关闭此连接或直接报错。)

If the application does not supply a Content-Length header, a server or gateway may choose one of several approaches to handling it. The simplest of these is to close the client connection when the response is completed. 如果应用没有提供Content-Length包头,服务端有好几种方法可以处理。最简单的一种是当响应结束后关闭客户端连接。

Under some circumstances, however, the server or gateway may be able to either generate a Content-Length header, or at least avoid the need to close the client connection. If the application does not call the write() callable, and returns an iterable whose len() is 1, then the server can automatically determine Content-Length by taking the length of the first bytestring yielded by the iterable. 但是在某些情况下,服务端要么必须自己生成一个Content-Length包头,或者至少避免关闭客户端连接。如果应用没有调用write()函数,且返回一个长度为1的迭代器,服务端应该根据迭代器生成的第一个字节流字符串自动确定Content-Length包头的值。

And, if the server and client both support HTTP/1.1 chunked encoding, then the server may use chunked encoding to send a chunk for each write() call or bytestring yielded by the iterable, thus generating a Content-Length header for each chunk. This allows the server to keep the client connection alive, if it wishes to do so. Note that the server must comply fully with RFC 2616 when doing this, or else fall back to one of the other strategies for dealing with the absence of Content-Length. 如果服务端和客户端都支持HTTP/1.1标准中的「整块编码」,则服务端可以使用「整块编码」在每次调用write()或迭代器每生成一个字节流字符串就发送一个数据块,同时为每一个数据块生成一个Content-Length包头。这种方法使服务端能够在需要的情况下保证客户端的连接不断。在采用这种方法时服务端必须遵守RFC2616,否则就只能使用其他方法来处理Content-Length包头缺失的情况。

(Note: applications and middleware must not apply any kind of Transfer-Encoding to their output, such as chunking or gzipping; as hop-by-hop operations, these encodings are the province of the actual web server/gateway. See Other HTTP Features below, for more details.) (注意:应用和中间件禁止在各自的输出中使用任何数据编码手段,比如分块或压缩;在进行「逐跳」操作时,这些编码方式是服务端的特权。参见「其他HTTP特性」小节以获取更多细节。)

Buffering and Streaming(缓冲和流)

Generally speaking, applications will achieve the best throughput by buffering their (modestly-sized) output and sending it all at once. This is a common approach in existing frameworks such as Zope: the output is buffered in a StringIO or similar object, then transmitted all at once, along with the response headers. 缓冲区和流处理一般来说,应用通过缓存适量的数据最后一次输出来达到最佳的吞吐量。这是现有框架比如Zope中常用的技术:输出被缓存在一个StringIO或类似对象中,最后与响应包头一起一次输出。

The corresponding approach in WSGI is for the application to simply return a single-element iterable (such as a list) containing the response body as a single bytestring. This is the recommended approach for the vast majority of application functions, that render HTML pages whose text easily fits in memory. WSGI中类似的处理是让应用简单地返回一个包含字节流字符串形式的响应包体的迭代器(比如一个列表)。对于绝大多数渲染数据量很小(足以放在内存中)的HTML页面的应用函数都建议采用这种方法。

For large files, however, or for specialized uses of HTTP streaming (such as multipart server push), an application may need to provide output in smaller blocks (e.g. to avoid loading a large file into memory). It's also sometimes the case that part of a response may be time-consuming to produce, but it would be useful to send ahead the portion of the response that precedes it. 但是对于大文件,或对于HTTP流的特殊用途(比如多部件「服务器推送」),应用可以根据需要将输出以小块形式提供(比如为了避免将一个大文件全部加载到内存中)。有时候响应的部分数据生成很耗时,也可以将已经生成的输出提前发送过去。

In these cases, applications will usually return an iterator (often a generator-iterator) that produces the output in a block-by-block fashion. These blocks may be broken to coincide with mulitpart boundaries (for server push), or just before time-consuming tasks (such as reading another block of an on-disk file). 在这些情况下,应用经常会返回一个生成迭代器来生成一块一块的输出。这些数据块可以被打散以适应多部件边界(比如「服务器推送」),或者只是在耗时很长的任务之前进行(比如读取另一个在磁盘上的文件数据块)。

WSGI servers, gateways, and middleware must not delay the transmission of any block; they must either fully transmit the block to the client, or guarantee that they will continue transmission even while the application is producing its next block. A server/gateway or middleware may provide this guarantee in one of three ways: 遵循WSGI的Web服务器,网关服务器和中间件不能延迟传送任何数据块。它们必须要么将整个数据块传送给客户端,要么保证即使应用在生成下一个数据块的时候它们仍然会继续传输,可以通过以下三种方法提供保证:

  1. Send the entire block to the operating system (and request that any O/S buffers be flushed) before returning control to the application, OR 将这个数据块转交给操作系统并请求刷新所有系统缓存。

  2. Use a different thread to ensure that the block continues to be transmitted while the application produces the next block. 使用另一个单独的线程保证数据块在应用生成下一个数据块的时候继续传送。

  3. (Middleware only) send the entire block to its parent gateway/server 中间件还可以将整个数据块传送给其上层的网关服务器或Web服务器。

By providing this guarantee, WSGI allows applications to ensure that transmission will not become stalled at an arbitrary point in their output data. This is critical for proper functioning of e.g. multipart server push streaming, where data between multipart boundaries should be transmitted in full to the client. 通过提供这个保证,WSGI保证数据传送不会再应用输出数据的某个时刻被打断。这对于诸如多部件「服务器推送」输出流之类技术有重要作用,在这些技术中多部件边间之间的数据要求必须完整地传送到客户端。

Middleware Handling of Block Boundaries(块边界的中间件处理)

In order to better support asynchronous applications and servers, middleware components must not block iteration waiting for multiple values from an application iterable. If the middleware needs to accumulate more data from the application before it can produce any output, it must yield an empty bytestring. 为了更好地支持异步的应用和服务器,中间件不能在等待应用迭代器生成多个值时阻塞迭代。如果中间件需要在应用尚未有输出的时候收集更多数据,则它必须生成一个空的字节流字符串。

To put this requirement another way, a middleware component must yield at least one value each time its underlying application yields a value. If the middleware cannot yield any other value, it must yield an empty bytestring. 换言之,每次下层应用产生一个值时中间件都必须相应生成至少一个值。如果中间件不能生成任何有意义的值,则生成一个空的字节流字符串。

This requirement ensures that asynchronous applications and servers can conspire to reduce the number of threads that are required to run a given number of application instances simultaneously. 这个规定保证了异步应用和服务器能够合作,以减少同时支持固定数量应用实例所需的线程数量。

Note also that this requirement means that middleware must return an iterable as soon as its underlying application returns an iterable. It is also forbidden for middleware to use the write() callable to transmit data that is yielded by an underlying application. Middleware may only use their parent server's write() callable to transmit data that the underlying application sent using a middleware-provided write() callable. 值得注意的是,这要求中间件必须在下层应用返回一个迭代器时也立即向上返回一个迭代器。中间件禁止使用write()可调用者来传送下层应用生成的数据。中间件只能调用上层服务器的write()可调用者来传送下层应用调用中间件自己的write()可调用者生成的数据。

The write() Callable(可调用 write())

Some existing application framework APIs support unbuffered output in a different manner than WSGI. Specifically, they provide a write function or method of some kind to write an unbuffered block of data, or else they provide a buffered write function and a flush mechanism to flush the buffer. 某些现有框架的API支持无缓冲输出的方法与WSGI不同。尤其是他们提供了某种形式的write函数以无缓冲地写入一个数据块,或者提供了一个有缓冲的write函数和一个flush机制来刷新缓存。

Unfortunately, such APIs cannot be implemented in terms of WSGI's iterable application return value, unless threads or other special mechanisms are used. 不幸的是这些API不能使用WSGI应用的迭代器返回值来实现,除非使用多线程或类似的特殊技术。

Therefore, to allow these frameworks to continue using an imperative API, WSGI includes a special write() callable, returned by the start_response callable. 因此为了让这些框架继续使用必要的API,WSGI包含了一个特别的write()可调用者,由start_response可调用者返回。

New WSGI applications and frameworks should not use the write() callable if it is possible to avoid doing so. The write() callable is strictly a hack to support imperative streaming APIs. In general, applications should produce their output via their returned iterable, as this makes it possible for web servers to interleave other tasks in the same Python thread, potentially providing better throughput for the server as a whole. 新的WSGI应用和框架在不必要的时候不应该使用write()可调用者。write()可调用者是为了支持必要的流式API的一种hack手段。一般而言,应用应该使用迭代器返回输出,这样Web服务器可以在同一个Python线程中交替完成不同的任务,从而潜在地提高服务器的吞吐量。

The write() callable is returned by the start_response() callable, and it accepts a single parameter: a bytestring to be written as part of the HTTP response body, that is treated exactly as though it had been yielded by the output iterable. In other words, before write() returns, it must guarantee that the passed-in bytestring was either completely sent to the client, or that it is buffered for transmission while the application proceeds onward. write()可调用者由start_response()可调用者返回,只接受一个参数:作为HTTP包体一部分的一个字节流字符串,并将此字符串当作由输出迭代器生成的。换言之,在write()返回前,必须保证传入的字节流字符串要么被完整地传送到了客户端,或者在应用继续运行的时候已被缓存起来等待传送。

An application must return an iterable object, even if it uses write() to produce all or part of its response body. The returned iterable may be empty (i.e. yield no non-empty bytestrings), but if it does yield non-empty bytestrings, that output must be treated normally by the server or gateway (i.e., it must be sent or queued immediately). Applications must not invoke write() from within their return iterable, and therefore any bytestrings yielded by the iterable are transmitted after all bytestrings passed to write() have been sent to the client. 应用必须返回一个迭代器对象,即使它使用了write()来生成全部或者部分的响应包体。返回的迭代器必须为空(即不生成任何非空字节流字符串),但是如果它确实生成了非空字节流字符串,则该输出必须被Web服务器或网关服务器当作一般输出处理(即必须被立即传送或缓存)。应用禁止在其返回的迭代器内部调用write(),因此任何迭代器生成的字节流字符串必须在所有传递给write()的字符串被发送给客户端之后才能进行传送。

Unicode Issues(Unicode的问题)

HTTP does not directly support Unicode, and neither does this interface. All encoding/decoding must be handled by the application; all strings passed to or from the server must be of type str or bytes, never unicode. The result of using a unicode object where a string object is required, is undefined. HTTP并不直接支持Unicode,WSGI亦然。所有的编码和解码工作由应用自己完成,所有发送给服务器或从服务器接收的字符串必须是str类型或bytes类型的,而不能是unicode类型。在要求使用字符串类型的地方使用unicode类型的结果是不可知的。

Note also that strings passed to start_response() as a status or as response headers must follow RFC 2616 with respect to encoding. That is, they must either be ISO-8859-1 characters, or use RFC 2047 MIME encoding. 作为状态或响应包头传递给start_response()的字符串必须遵守RFC2616中关于编码的规定,即要么是ISO-8859-1字符,要么使用RFC2047MIME编码。

On Python platforms where the str or StringType type is in fact Unicode-based (e.g. Jython, IronPython, Python 3, etc.), all strings referred to in this specification must contain only code points representable in ISO-8859-1 encoding (\u0000 through \u00FF, inclusive). It is a fatal error for an application to supply strings containing any other Unicode character or code point. Similarly, servers and gateways must not supply strings to an application containing any other Unicode characters. 在str或StringType类型实际上是Unicode编码的Python平台(如Jython,IronPython,Python3等)上,所有对应于本规范的「字符串」只能包含对应于那些ISO-8859-1可表示编码点的Unicode编码(即u0000到u00FF)。应用使用的字符串中包含有其他字符或编码点会引发致命错误。Web服务器和网关服务器也禁止提供包含其他Unicode字符的字符串。

Again, all objects referred to in this specification as strings must be of type str or StringType, and must not be of type unicode or UnicodeType. And, even if a given platform allows for more than 8 bits per character in str/StringType objects, only the lower 8 bits may be used, for any value referred to in this specification as a string. 再强调一遍,所有对应到本规范的「字符串」必须是str类型或者StringType类型,而不能是unicode类型或者UnicodeType类型。即使已有的平台允许在str或StringType类型中使用每个字符多于8比特的编码,也只有低8位允许使用。

For values referred to in this specification as bytestrings (i.e., values read from wsgi.input, passed to write() or yielded by the application), the value must be of type bytes under Python 3, and str in earlier versions of Python. 本规范中所谓的「字节流字符串」(即从wsgi.input中读入的值,最后会传递给write()或由应用生成),其值必须是Python3下的bytes类型,或者更低版本Python中的str类型。

Error Handling(错误处理)

In general, applications should try to trap their own, internal errors, and display a helpful message in the browser. (It is up to the application to decide what helpful means in this context.) 一般而言,应用应该自己捕获内部错误,并在浏览器中显示有帮助的错误信息。(由应用自己决定什么叫「有帮助」。)

However, to display such a message, the application must not have actually sent any data to the browser yet, or else it risks corrupting the response. WSGI therefore provides a mechanism to either allow the application to send its error message, or be automatically aborted: the exc_info argument to start_response. Here is an example of its use: 但是要显示这条信息,应用在之前必须没有发送任何数据到浏览器,或者可以冒险中断响应。WSGI提供了一个机制以使应用要么能够传送错误信息,要么会被自动终止:通过start_response的exc_info参数。下面有一个例子来阐述其用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
try:
# regular application code here
status = "200 Froody"
response_headers = [("content-type", "text/plain")]
start_response(status, response_headers)
return ["normal body goes here"]
except:
# XXX should trap runtime issues like MemoryError, KeyboardInterrupt
# in a separate handler before this bare 'except:'...
status = "500 Oops"
response_headers = [("content-type", "text/plain")]
start_response(status, response_headers, sys.exc_info())
return ["error body goes here"]
If no output has been written when an exception occurs, the call to start_response will return normally, and the application will return an error body to be sent to the browser. However, if any output has already been sent to the browser, start_response will reraise the provided exception. This exception should not be trapped by the application, and so the application will abort. The server or gateway can then trap this (fatal) exception and abort the response. 如果在异常发生时还没有任何输出,start_response的调用会正常返回,应用会收到可用以传递给浏览器的错误信息。而如果之前有任何输出已经被传递给浏览器,start_response会重新抛出异常。这个异常不能被应用捕获,所以应用不会终止。Web服务器或网关服务器能够捕获这个异常并终止响应。

Servers should trap and log any exception that aborts an application or the iteration of its return value. If a partial response has already been written to the browser when an application error occurs, the server or gateway may attempt to add an error message to the output, if the already-sent headers indicate a text/* content type that the server knows how to modify cleanly. 服务器应该捕获并记录所有终止了应用或其返回值迭代过程的异常。如果错误发生时部分响应信息已经被传递给浏览器,Web服务器或网关服务器可以尝试在输出中添加一条错误信息,只要已发送的包头包含服务器可以显式修改的text/*类型内容。

Some middleware may wish to provide additional exception handling services, or intercept and replace application error messages. In such cases, middleware may choose to not re-raise the exc_info supplied to start_response, but instead raise a middleware-specific exception, or simply return without an exception after storing the supplied arguments. This will then cause the application to return its error body iterable (or invoke write()), allowing the middleware to capture and modify the error output. These techniques will work as long as application authors: 某些中间件可能希望能够提供其他的错误处理机制,或拦截和替换应用错误信息。在这种情况下,中间件可以选择不重新将exc_info抛出给start_response,但是必须相应地抛出一个中间件特定的异常,或者缓存下提供的参数后简单地正常返回。这迫使应用返回其错误信息迭代器(或调用write()),从而使中间件能够捕获和修改错误信息。这些技术要求开发者遵循如下规范:

  1. Always provide exc_info when beginning an error response 开始错误响应时总是提供exc_info参数。

  2. Never trap errors raised by start_response when exc_info is being provided 提供了exc_info参数的情况下不要捕获任何由start_response抛出的异常。

HTTP 1.1 Expect/Continue(HTTP 1.1预期/继续)

Servers and gateways that implement HTTP 1.1 must provide transparent support for HTTP 1.1’s expect/continue mechanism. This may be done in any of several ways: 实现了HTTP1.1标准的Web服务器和网关服务器必须提供HTTP1.1标准中「expect/continue」机制的透明支持。这可以通过以下方法做到:

  1. Respond to requests containing an Expect: 100-continue request with an immediate 100 Continue response, and proceed normally. 对于任何「Expect:100-continue」的请求返回一个即时的「100Continue」响应,然后正常继续运行。

  2. Proceed with the request normally, but provide the application with a wsgi.input stream that will send the 100 Continue response if/when the application first attempts to read from the input stream. The read request must then remain blocked until the client responds. 继续正常运行,但是提供给应用一个wsgi.input流,这个流会在应用第一次尝试读取输入流的时候发送「100Continue」响应。读请求之后必须阻塞,直到客户端响应为止。

  3. Wait until the client decides that the server does not support expect/continue, and sends the request body on its own. (This is suboptimal, and is not recommended.) 阻塞请求直到客户端意识到服务器不支持expect/continue机制,然后自己发送请求包体。(这种方法不是最优的,不推荐使用。)

Note that these behavior restrictions do not apply for HTTP 1.0 requests, or for requests that are not directed to an application object. For more information on HTTP 1.1 Expect/Continue, see RFC 2616, sections 8.2.3 and 10.1.1. 注意:这些限制并不针对HTTP1.0的请求,也不适用于不传递给应用对象的请求。参见RFC26168.2.3小节和10.1.1小节以获取更多关于HTTP1.1Expect/Continue请求的信息。

Other HTTP Features(其他HTTP特性)

In general, servers and gateways should play dumb and allow the application complete control over its output. They should only make changes that do not alter the effective semantics of the application's response. It is always possible for the application developer to add middleware components to supply additional features, so server/gateway developers should be conservative in their implementation. In a sense, a server should consider itself to be like an HTTP gateway server, with the application being an HTTP origin server. (See RFC 2616, section 1.3, for the definition of these terms.) 其他HTTP特性一般来说,服务端应该让应用全权负责控制自己的输出。服务端只能进行不影响应用响应语义的改动。应用开发者总是可以通过添加中间件来提供附件特性,因此服务端开发者在实现过程中必须尽可能保守。从某种意义上来说,Web服务器应当视自己为HTTP「网关服务器」,而将应用看成一个HTTP的「源服务器」。(参见RFC26161.3小节获取更多信息。)

However, because WSGI servers and applications do not communicate via HTTP, what RFC 2616 calls hop-by-hop headers do not apply to WSGI internal communications. WSGI applications must not generate any hop-by-hop headers, attempt to use HTTP features that would require them to generate such headers, or rely on the content of any incoming hop-by-hop headers in the environ dictionary. WSGI servers must handle any supported inbound hop-by-hop headers on their own, such as by decoding any inbound Transfer-Encoding, including chunked encoding if applicable. 但是因为WSGI服务端和应用端不通过HTTP交互,所以RFC2616称之为「逐跳」包头的特性不适用与WSGI的内部通信。WSGI应用不能生成任何「逐跳」包头[7],不能使用任何需要生成该包头的HTTP特性,也不能依赖于environ字典中的任何传入的「逐跳」包头。WSGI服务端必须自己处理任何传入的能够支持的「逐跳」包头,比如对传入的Transfer-Encoding进行解码,如果可能的话其中也包括整块编码。

Applying these principles to a variety of HTTP features, it should be clear that a server may handle cache validation via the If-None-Match and If-Modified-Since request headers and the Last-Modified and ETag response headers. However, it is not required to do this, and the application should perform its own cache validation if it wants to support that feature, since the server/gateway is not required to do such validation. 以上的原则适用于很多HTTP特性,服务端可以通过If-None-Match和If-Modified-Since请求包头以及Last-Modified和ETag响应包头来处理缓存生效的问题。但是这并不是必要的,应用如果想支持该特性应该自己处理自己的缓存生效问题,因为服务端不一定会处理。

Similarly, a server may re-encode or transport-encode an application's response, but the application should use a suitable content encoding on its own, and must not apply a transport encoding. A server may transmit byte ranges of the application's response if requested by the client, and the application doesn't natively support byte ranges. Again, however, the application should perform this function on its own if desired. 类似情况比如服务端可以对应用的响应进行重新编码或传输编码,但是应用应该自己选择一个合适的内容编码方式,并且禁止使用传输编码。服务端可以在客户端要求的时候传输应用响应的字节范围(?),而应用并不原生支持字节范围,但是同样的,应用应该在有需求时自己干这个。

Note that these restrictions on applications do not necessarily mean that every application must reimplement every HTTP feature; many HTTP features can be partially or fully implemented by middleware components, thus freeing both server and application authors from implementing the same features over and over again. 请注意,这些限制条件并不是要求应用把每一个HTTP特性都自己重新实现一遍。很多特性可以部分或全部被中间件实现,从而避免服务端和应用端的开发者一次又一次地重复实现同样的特性。

Thread Support(线程的支持)

Thread support, or lack thereof, is also server-dependent. Servers that can run multiple requests in parallel, should also provide the option of running an application in a single-threaded fashion, so that applications or frameworks that are not thread-safe may still be used with that server. 线程机制的支持与否取决与各Web服务器自身。可以并行处理多个请求的服务器,必须提供单线程运行应用的选项,以使非线程安全的应用或框架仍然能够在其上运行。

Implementation/Application Notes(实现/应用笔记)

Server Extension APIs(服务器扩展接口)

Some server authors may wish to expose more advanced APIs, that application or framework authors can use for specialized purposes. For example, a gateway based on mod_python might wish to expose part of the Apache API as a WSGI extension. 一些服务端开发者希望暴露更多的高级API,以使应用端开发者用来实现特殊的需求。比如说一个基于mod_python的网关服务器会希望以WSGI扩展的方式暴露部分Apache的API。

In the simplest case, this requires nothing more than defining an environ variable, such as mod_python.some_api. But, in many cases, the possible presence of middleware can make this difficult. For example, an API that offers access to the same HTTP headers that are found in environ variables, might return different data if environ has been modified by middleware. 在最简单的情况下,这只要求定义一个environ变量,比如mod_python.some_api。但是很多时候可能存在的中间件会使情况变得复杂起来。比如一个environ变量中提供访问某个HTTP包头功能的API,可能在environ被中间件修改之后返回不同的值。

In general, any extension API that duplicates, supplants, or bypasses some portion of WSGI functionality runs the risk of being incompatible with middleware components. Server/gateway developers should not assume that nobody will use middleware, because some framework developers specifically intend to organize or reorganize their frameworks to function almost entirely as middleware of various kinds. 一般而言,任何复制、补足或绕过了部分WSGI功能的API都有与中间件不兼容的危险。服务端开发者不应该假设没有使用中间件,因为某些框架开发者尤其希望将他们的框架设计或重构成类似中间件的样子。

So, to provide maximum compatibility, servers and gateways that provide extension APIs that replace some WSGI functionality, must design those APIs so that they are invoked using the portion of the API that they replace. For example, an extension API to access HTTP request headers must require the application to pass in its current environ, so that the server/gateway may verify that HTTP headers accessible via the API have not been altered by middleware. If the extension API cannot guarantee that it will always agree with environ about the contents of HTTP headers, it must refuse service to the application, e.g. by raising an error, returning None instead of a header collection, or whatever is appropriate to the API. 所以为了提供最大程度的兼容性,提供扩展API以取代某些WSGI功能的服务端应该精心设计,以使它们在被调用时使用了这些扩展API。举例来说,一个访问HTTP请求包头的API必须要求应用传递其当前的environ,以使服务端能确定通过该API能访问到的HTTP包头没有被中间件修改。如果扩展API不能保证其对HTTP包头的要求与environ一致,那么它必须通过报错、返回None而不是包头集合或任何其他合适的方式拒绝为应用服务。

Similarly, if an extension API provides an alternate means of writing response data or headers, it should require the start_response callable to be passed in, before the application can obtain the extended service. If the object passed in is not the same one that the server/gateway originally supplied to the application, it cannot guarantee correct operation and must refuse to provide the extended service to the application. 类似的例子还有如果扩展API提供写入响应数据或包头的功能,它必须要求应用在使用扩展功能之前传入start_response可调用者。如果该可调用者与服务端最早从应用那儿收到的不一致,那么该API便不能保证正确的响应,只能拒绝为应用提供改扩展服务。

These guidelines also apply to middleware that adds information such as parsed cookies, form variables, sessions, and the like to environ. Specifically, such middleware should provide these features as functions which operate on environ, rather than simply stuffing values into environ. This helps ensure that information is calculated from environ after any middleware has done any URL rewrites or other environ modifications. 这些指导原则也适用于在environ中额外添加了类似解析过的cookie、构造变量、会话等内容的中间件。尤其是那些将这些功能以作用于environ的函数形式提供的中间件,相比简单将数据插入environ中的中间件更要注意。这保证了在每次中间件对environ进行了URL重写或其他修改之后environ中的信息都会被检查一遍。

It is very important that these safe extension rules be followed by both server/gateway and middleware developers, in order to avoid a future in which middleware developers are forced to delete any and all extension APIs from environ to ensure that their mediation isn't being bypassed by applications using those extensions! 这些「安全扩展」的原则非常重要,服务端和应用端开发者都应该遵守,以避免未来的某个时候中间件开发者不得不删除某些或全部涉及environ的扩展API,以免中间件的功能因为应用调用了扩展API而失效。

Application Configuration(应用程序配置)

This specification does not define how a server selects or obtains an application to invoke. These and other configuration options are highly server-specific matters. It is expected that server/gateway authors will document how to configure the server to execute a particular application object, and with what options (such as threading options). 本规范并没有定义服务端如何选择或获取一个应用来调用。这些以及其他的配置选项是由服务端根据自己的特定情况决定的。服务端开发者应该在自述文档中记述如何配置才能以特定的选项(如线程选项)执行一个特定的应用程序对象。

Framework authors, on the other hand, should document how to create an application object that wraps their framework's functionality. The user, who has chosen both the server and the application framework, must connect the two together. However, since both the framework and the server now have a common interface, this should be merely a mechanical matter, rather than a significant engineering effort for each new server/framework pair. 另一方面,框架开发者应该在自述文档中记载如何创建一个包含框架功能的应用对象。在服务端和应用端都使用了框架的用户必须将二者结合起来考虑。但是由于双方现在都有通用接口了,这只是个体力活儿,而不是一个重要的工程难题。

Finally, some applications, frameworks, and middleware may wish to use the environ dictionary to receive simple string configuration options. Servers and gateways should support this by allowing an application's deployer to specify name-value pairs to be placed in environ. In the simplest case, this support can consist merely of copying all operating system-supplied environment variables from os.environ into the environ dictionary, since the deployer in principle can configure these externally to the server, or in the CGI case they may be able to be set via the server's configuration files. 最后,有些应用、框架和中间件希望使用environ字典来收取简单的配置选项字符串。Web服务器和网关服务器应该通过允许应用开发者在environ中指定键值对来支持这个特性。最简单的情况下,只需要从os.environ中拷贝所有操作系统提供的环境变量到environ字典中即可,因为部署人员原则上能够在服务器上手工配置这些变量,或者在CGI环境下他们可以通过服务器的配置文件来完成。

Applications should try to keep such required variables to a minimum, since not all servers will support easy configuration of them. Of course, even in the worst case, persons deploying an application can create a script to supply the necessary configuration values: 应用应该尽量少使用这些变量,因为不是所有的服务器都能够很方便地配置它们。当然,即使在最坏的情况下,部署应用的人也能够通过创建一个脚本提供必要配置选项:

1
2
3
4
5
from the_app import application

def new_app(environ, start_response):
environ['the_app.configval1'] = 'something'
return application(environ, start_response)
But, most existing applications and frameworks will probably only need a single configuration value from environ, to indicate the location of their application or framework-specific configuration file(s). (Of course, applications should cache such configuration, to avoid having to re-read it upon each invocation.) 但是大部分的应用和框架可能只需要environ中的一个配置域来显示应用或框架用到的配置文件路径。(当然,应用可以缓存这些配置来避免在每次调用中都读一遍。)

URL Reconstruction(URL重建)

If an application wishes to reconstruct a request's complete URL, it may do so using the following algorithm, contributed by Ian Bicking: 如果应用希望重建一个请求的完整URL,可以通过下面由IanBicking提供的算法来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from urllib.parse import quote
url = environ['wsgi.url_scheme']+'://'

if environ.get('HTTP_HOST'):
url += environ['HTTP_HOST']
else:
url += environ['SERVER_NAME']

if environ['wsgi.url_scheme'] == 'https':
if environ['SERVER_PORT'] != '443':
url += ':' + environ['SERVER_PORT']
else:
if environ['SERVER_PORT'] != '80':
url += ':' + environ['SERVER_PORT']

url += quote(environ.get('SCRIPT_NAME', ''))
url += quote(environ.get('PATH_INFO', ''))
if environ.get('QUERY_STRING'):
url += '?' + environ['QUERY_STRING']
Note that such a reconstructed URL may not be precisely the same URI as requested by the client. Server rewrite rules, for example, may have modified the client's originally requested URL to place it in a canonical form. 注意重建出来的URL可能不是客户端请求的那个URI,比如服务器重写规则可能会修改客户端请求的原始URL以使其符合规范。

Supporting Older (<2.2) Versions of Python(支持更早版本(<2.2)的Python)

Some servers, gateways, or applications may wish to support older (<2.2) versions of Python. This is especially important if Jython is a target platform, since as of this writing a production-ready version of Jython 2.2 is not yet available. 支持低于2.2版本的Python某些Web服务器、网关服务器或者应用可能会需要支持低于2.2版本的Python。在使用Jython作为平台的时候这一点尤其重要,因为高于2.2版本的Jython还不能在生产环境中应用。

For servers and gateways, this is relatively straightforward: servers and gateways targeting pre-2.2 versions of Python must simply restrict themselves to using only a standard for loop to iterate over any iterable returned by an application. This is the only way to ensure source-level compatibility with both the pre-2.2 iterator protocol (discussed further below) and today's iterator protocol (see PEP 234). 对于Web服务器和网关服务器,这种支持相对直接:目标平台是低于2.2版本的Python的服务器和网关只能使用一个标准的for循环来迭代任何应用返回的迭代器。这是唯一保证各版本间的迭代器协议在源码级兼容的方法,后面我们会详细讨论。(最新的迭代器协议见PEP234。)

(Note that this technique necessarily applies only to servers, gateways, or middleware that are written in Python. Discussion of how to use iterator protocol(s) correctly from other languages is outside the scope of this PEP.) (注意这个技术只适用于Python下的Web服务器、网关服务器和中间件。其他语言中的迭代器协议如何正确使用超出了本规范的讨论范围。)

For applications, supporting pre-2.2 versions of Python is slightly more complex: 对于应用程序,支持低于2.2版本的Python有一点麻烦:

  • You may not return a file object and expect it to work as an iterable, since before Python 2.2, files were not iterable. (In general, you shouldn't do this anyway, because it will perform quite poorly most of the time!) Use wsgi.file_wrapper or an application-specific file wrapper class. (See Optional Platform-Specific File Handling for more on wsgi.file_wrapper, and an example class you can use to wrap a file as an iterable.) 你不能返回一个文件对象并期望它像一个迭代器一样工作,因为从Python2.2开始文件就不是迭代器了。(一般而言你也不应该使用这种方法,因为绝大多数情况下这是一种丑陋的实现!)应该使用wsgi.file_wrapper或者应用指定的文件包装器。(参见(可选)特定平台上的文件处理小节以获取更多文件包装器的信息,以及一个可以用来将文件包装为迭代器的样例类。

  • If you return a custom iterable, it must implement the pre-2.2 iterator protocol. That is, provide a __getitem__ method that accepts an integer key, and raises IndexError when exhausted. (Note that built-in sequence types are also acceptable, since they also implement this protocol.) 如果你返回一个经过定制的迭代器,它必须实现2.2版本之前的迭代器协议。亦即提供一个__getitem__方法,这个方法接受一个整数键值,当该值耗尽时就会抛出IndexError异常。(内建的序列类型也是可接受的,因为它们已经集成了相关协议。)

Finally, middleware that wishes to support pre-2.2 versions of Python, and iterates over application return values or itself returns an iterable (or both), must follow the appropriate recommendations above. 最后,希望支持低于2.2版本的Python且迭代应用返回值或本身返回一个迭代器的中间件必须遵守以上提到的相应的推荐方法。

(Note: It should go without saying that to support pre-2.2 versions of Python, any server, gateway, application, or middleware must also use only language features available in the target version, use 1 and 0 instead of True and False, etc.) (注意:Web服务器、网关服务器、应用或者中间件在支持低于2.2版本的Python都必须只使用该版本支持的特性,比如使用1和0来代替True和False等。)

Optional Platform-Specific File Handling(可选的特定于平台的文件处理)

Some operating environments provide special high-performance file- transmission facilities, such as the Unix sendfile() call. Servers and gateways may expose this functionality via an optional wsgi.file_wrapper key in the environ. An application may use this file wrapper to convert a file or file-like object into an iterable that it then returns, e.g.: 某些操作系统提供特殊的高性能文件传输功能,比如Unix的sendfile()系统调用。Web服务器和网关服务器可以通过environ中可选的wsgi.file_wrapper域值来提供此功能。应用可以使用这种「文件包装器」来将一个文件或类文件对象转换为一个迭代器并返回,如下所示:

1
2
3
4
if 'wsgi.file_wrapper' in environ:
return environ['wsgi.file_wrapper'](filelike, block_size)
else:
return iter(lambda: filelike.read(block_size), '')
If the server or gateway supplies wsgi.file_wrapper, it must be a callable that accepts one required positional parameter, and one optional positional parameter. The first parameter is the file-like object to be sent, and the second parameter is an optional block size suggestion (which the server/gateway need not use). The callable must return an iterable object, and must not perform any data transmission until and unless the server/gateway actually receives the iterable as a return value from the application. (To do otherwise would prevent middleware from being able to interpret or override the response data.) 如果Web服务器或网关服务器支持wsgi.file_wrapper,则它必须是一个可调用者,接收一个必须的形式参数和一个可选的形式参数。第一个形式参数是一个待发送的类文件对象,第二个可选的则是一个建议的块大小(服务端不一定要采纳)。这个可调用者必须返回一个迭代对象,并且禁止在服务端实际接收到该迭代器返回值之前传送任何数据。(不这样做的话会妨碍中间件对响应数据进行译码或修改。)

To be considered file-like, the object supplied by the application must have a read() method that takes an optional size argument. It may have a close() method, and if so, the iterable returned by wsgi.file_wrapper must have a close() method that invokes the original file-like object's close() method. If the file-like object has any other methods or attributes with names matching those of Python built-in file objects (e.g. fileno()), the wsgi.file_wrapper may assume that these methods or attributes have the same semantics as those of a built-in file object. 为了被看做一个文件,应用提供的对象必须有一个能接受一个可选文件大小参数的read()方法。该对象也可以有一个close()方法,如果提供了这个方法,wsgi.file_wrapper返回的迭代器就必须提供一个close()方法,这个方法最终调用了对象提供的close()方法。如果该对象提供了任何与Python内建文件对象名字一样的方法或属性(比如fileno()),wsgi.file_wrapper可以假设这些方法和属性与它们作为内建的方法和属性时语义相同。

The actual implementation of any platform-specific file handling must occur after the application returns, and the server or gateway checks to see if a wrapper object was returned. (Again, because of the presence of middleware, error handlers, and the like, it is not guaranteed that any wrapper created will actually be used.) 任何平台相关的文件处理必须是现在应用返回之后,并且由Web服务器和网关服务器来检查包装器对象是否返回了。(再强调一遍,由于中间件、错误处理程序之类的存在,并不保证包装器被创建了就一定会被使用。)

Apart from the handling of close(), the semantics of returning a file wrapper from the application should be the same as if the application had returned iter(filelike.read, ). In other words, transmission should begin at the current position within the file at the time that transmission begins, and continue until the end is reached, or until Content-Length bytes have been written. (If the application doesn't supply a Content-Length, the server may generate one from the file using its knowledge of the underlying file implementation.) 除了对于close()的处理,应用返回文件包装器的语义应该与应用返回iter(filelike.read,‘’)一样。换言之,数据传输应该从当前的文件读写指针位置开始,直到到达文件尾或者达到Content-Length要求的字节数。(如果应用没有提供Content-Length包头,服务端可以根据自己的文件实现机制对具体的文件生成一个。)

Of course, platform-specific file transmission APIs don't usually accept arbitrary file-like objects. Therefore, a wsgi.file_wrapper has to introspect the supplied object for things such as a fileno() (Unix-like OSes) or a java.nio.FileChannel (under Jython) in order to determine if the file-like object is suitable for use with the platform-specific API it supports. 当然,平台相关的文件传送API一般不会随便接受一个类文件对象。因此wsgi.file_wrapper必须自己检查所提供的对象有没有诸如fileno()(在Unix类系统上)或java.nio.FileChannel(在Jython平台上)之类的东西,以保证类文件对象正确使用了平台特有的API。

Note that even if the object is not suitable for the platform API, the wsgi.file_wrapper must still return an iterable that wraps read() and close(), so that applications using file wrappers are portable across platforms. Here's a simple platform-agnostic file wrapper class, suitable for old (pre 2.2) and new Pythons alike: 另外要注意的是即使该对象不适应平台特有的API,wsgi.file_wrapper也必须返回一个包装了read()和close()的迭代器,以使使用文件包装器的应用能够跨平台移植。下面有一个简单的不依赖特定平台的文件包装器类,适用于所有版本的Python:

1
2
3
4
5
6
7
8
9
10
11
12
13
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
and here is a snippet from a server/gateway that uses it to provide access to a platform-specific API: 下面一段代码是从服务端代码中抽出来的,支持访问平台相关的API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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()

0%