http://blog.csdn.net/on_1y/article/details/18818081
http://blog.csdn.net/on_1y/article/details/18803563
wsgi有两方,服务器方 和 应用程序
①服务器方:其调用应用程序,给应用程序提供(环境信息)和(回调函数), 这个回调函数是用来将应用程序设置的http header和status等信息传递给服务器方.
②应用程序:用来生成返回的header,body和status,以便返回给服务器方。
用Python语言写的一个符合WSGI的“Hello World”应用程序如下所示:
<span class="kw1" style="color:#ff770;font-weight: bold;">def</span> app<span class="br0">(</span>environ<span class="sy0" style="color:#66cc66;">,</span> start_response<span class="br0">)</span>: start_response<span class="br0">(</span><span class="st0" style="color:#483d8b;">'200 OK'</span><span class="sy0" style="color:#66cc66;">,</span> <span class="br0">[</span><span class="br0">(</span><span class="st0" style="color:#483d8b;">'Content-Type'</span><span class="sy0" style="color:#66cc66;">,</span> <span class="st0" style="color:#483d8b;">'text/plain'</span><span class="br0">)</span><span class="br0">]</span><span class="br0">)</span> <span class="kw1" style="color:#ff770;font-weight: bold;">yield</span> <span class="st0" style="color:#483d8b;">"Hello world!<span class="es0" style="color:#0099;font-weight: bold;">\n</span>"</span>
其中
- 第一行定义了一个名为app的应用程序,接受两个参数,environ和start_response,environ是一个字典包含了CGI中的环境变量,start_response也是一个callable,接受两个必须的参数,status(HTTP状态)和response_headers(响应消息的头)。
- 第二行调用了start_response,状态指定为“200 OK”,消息头指定为内容类型是“text/plain”
- 第三行将响应消息的消息体返回。
WSGI应用
WSGI应用其实就是一个callable的对象。举一个最简单的例子,假设存在如下的一个应用:
1
2
3
4
5
6
7
8
|
def application(environ, start_response):
status = '200 OK'
output = 'World!'
response_headers = [( 'Content-type' , 'text/plain' ),
( 'Content-Length' , str ( 12 )]
write = start_response(status, response_headers)
write( 'Hello ' )
return [output]
|
这个WSGI应用简单的可以用简陋来形容,但是他的确是一个功能完整的WSGI应用。只不过给人留下了太多的疑点,environ是什么?start_response是什么?为什么可以同时用write和return来返回内容?
对于这些疑问,不妨自己猜测一下他的作用。联想到CGI,那么environ可能就是一系列的环境变量,用来表示HTTP请求的信息,比如说method 之类的。start_response,可能是接受HTTP response头信息,然后返回一个write函数,这个write函数可以把HTTP response的body返回给客户端。return自然是将HTTP response的body信息返回。不过这里的write和函数返回有什么区别?会不会是其实外围默认调用write对应用返回值进行处理?而且为什么 应用的返回值是一个列表呢?说明肯定存在一个对应用执行结果的迭代输出过程。难道说他隐含的支持iterator或者generator吗?
等等,应用执行结果?一个应用既然是一个函数,说明肯定有一个对象去执行它,并且可以猜到,这个对象把environ和start_response传给应用,将应用的返回结果输出给客户端。那么这个对象是什么呢?自然就是WSGI容器了。
注意:以 点 开始的解释是WSGI规定 必须满足 的。
应用程序
- 应用程序是可调用对象
- 可调用对象有两个位置参数
所谓位置参数就是调用的时候,依靠位置来确定参数的语义,而不是参数名,也就是说服务 器调用应用程序时,应该是这样:
application(env, start_response)
而不是这样:
application(start_response=start_response, environ=env)
所以,参数名其实是可以随便起的,只不过为了表义清楚,我们起了environ
和 start_response
。
- 第一个参数environ是Python内置的dict对象,应用程序可以对这个参数任意修改。
- environ参数必须包含 WSGI 需要的一些变量(详见后文)
也可以包含一些扩展参数,命名规范见后文 - start_response参数是一个可调用对象。接受两个位置参数,一个可选参数。 例如:
start_response(status, response_headers, exc_info=None)
status参数是状态码,例如200 OK
。
response_headers参数是一个列表,列表项的形式为(header_name, header_value)。
exc_info参数在错误处理的时候使用。
status和response_headers的具体内容可以参考 HTTP 协议 Response部分。
- start_response必须返回一个可调用对象:
write(body_data)
- 应用程序必须返回一个可迭代对象。
- 应用程序不应假设返回的可迭代对象被遍历至终止,因为遍历过程可能出现错误。
- 应用程序必须在第一次返回可迭代数据之前调用 start_response 方法。
这是因为可迭代数据是 返回数据的body
部分,在它返回之前,需要使用start_response
返回 response_headers 数据。
start_response是HTTP响应的开始,它的形式为:
start_response(status, response_headers, exc_info=None)
返回一个可调用对象,这个可调用对象形式为:
write(body_data)
status 表示 HTTP 状态码,例如 "200 OK", "404 Not Found",它们在 RFC 2616中定义,status禁止包含控制字符。
response_headers 是一个列表,列表项是一个二元组: (header_name, heaer_value) , 每个 header_name 都必须是 RFC 2616 4.2 节中定义的HTTP 头部名。header_value 禁止包含控制字符。
另外,服务器程序必须保证正确的headers 被返回给客户端,如果应用程序没有返回headers,服务器必须添加它。
应用程序和middleware禁止使用 HTTP/1.1 中的 "hop-by-hop"特性,以及其它可能影响客户端与服务器永久连接的特性。
start_response 被调用时,服务器应该检查 headers 中的错误,另外,禁止 start_response直接将 response_headers传递给客户端,它必须把它们存储起来,一直到应用程序第一次迭代返回一个非空数据后,才能将response_headers传递给客户端。这其实是在说,HTTP响应body部分必须有数据,不能只返回一个header。
start_response的第三个参数是一个可选参数,exc_info,它必须和Python的 sys.exc_info()返回的数据有相同类型。当处理请求的过程遇到错误时,这个参数会被设置,同时调用 start_response。如果提供了exc_info,但是HTTP headers 还没有输出,那么 start_response需要将当前存储的 HTTP response headers替换成一个新值。但是,如果提供了exc_info,同时 HTTP headers已经输出了,那么 start_response 必须 raise 一个 error。禁止应用程序处理 start_response raise出的 exceptions,应该交给服务器程序处理。
当且仅当提供 exc_info参数时,start_response才可以被调用多于一次。换句话说,要是没提供这个参数,start_response在当前应用程序中调用后,禁止再调用。
为了避免循环引用,start_response实现时需要保证 exc_info在函数调用后不再包含引用。 也就是说start_response用完 exc_info后,需要保证执行一句
exc_info = None
这可以通过 try/finally实现。