摘要
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.
为促进python的web应用在各类web服务器上的移植, 本文档指定了web服务器和python的web框架间的标准接口.
原理和目标
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 [1]. 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.
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.
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.
python目前拥有了大量的web框架,例如Zope, Quixote, Webware, SkunkWeb, PSO, Twisted Web.(想看更多点击http://wiki.python.org/moin/WebFrameworks .). 从如此多的框架中选择对刚刚进入python的新人来说是个问题,一般来说, 选择一个web框架限制了对web服务器的选择,反过来也一样.
相比之下,尽管 Java也有众多的web框架, Java的 servlet API使得程序员可以使用任何Java web框架编写在能运行在任何web服务器上的程序,只要他们都支持servlet API.
概述
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.
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.
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.
WSGI接口有两个方面, server 或 gateway 服务端, 以及 application 或 framework 应用端. 服务端会调用一个应用端的可调用对象, 如何传递可调用对象的细节由服务端决定.有可能一些服务器要求应用程序的开发者写一个脚本来创建服务器/网关的一个实例来获得应用程序;另一些服务器可能使用配置文件或者其他的机制来制定哪里有一个需要被import/ obtain 的应用程序.
除了这些服务器,网管,应用程序和框架外,也可以创建了实现WSGI随意一侧接口的中间件组件.这些组件从服务器看就像应用,从应用程序看就像服务器,利用这样的组建可以提供更多的接口, 内容转换, 导航等等其他有用的功能.
在整个概述中,我们使用术语"callable"来表示一个函数,方法, 类或者一个实现了__call__方法的实例.可调用对象的实现要根据服务端和应用端的需求来选择合适的方法.相反, 服务端和应用端调用这个可调用对象的时候并不用去关心到底是谁提供给它的, 仅仅是调用它们而已, 而不会对它们内省.
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.
(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.)
Here are two example application objects; one is a function, and the other is a class:
应用程序对象是一个需要接受两个参数的可调用对象. 这个术语"object(对象)"不应该被误解为需要一个对象实例: 函数,方法, 类, 实现了__call__方法的实例都可以作为一个应用程序对象来使用.应用程序对象必须能够被多次调用, 几乎所有的服务器/网关(除了CGI)都会制造重复请求.
(注意: 尽管我们一直说应用程序对象, 这不应该被理解为, 假设应用开发者使用已存在的高等web框架服务来开发他们的应用, 就必须使用WSGI作为web编程的API!WSGI对于框架和服务器开发者仅仅是一个工具,并没想要直接支持应用开发人员.)
这有两个例子,一个是函数,另一个是类:
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!\n']
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!\n"
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服务器上记录下来.
import os, sys def run_with_cgi(application): environ = dict(os.environ.items()) environ['wsgi.input'] = sys.stdin 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): 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 sys.stdout.write('Status: %s\r\n' % status) for header in response_headers: sys.stdout.write('%s: %s\r\n' % header) sys.stdout.write('\r\n') sys.stdout.write(data) sys.stdout.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[0], exc_info[1], exc_info[2] finally: exc_info = None # avoid dangling circular ref elif headers_set: raise AssertionError("Headers already set!") headers_set[:] = [status,response_headers] 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.
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
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".
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.
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.)
请注意一个单独的对象可能对于应用端扮演服务器的角色,对于服务端又扮演应用程序的角色. 这样的中间件可以实现这件功能:
1. 根据目标地址将请求发送给不同的应用程序对象,并在之后重写环境变量.
2. 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
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 string, 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()) 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 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)) 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))