WSGI的由来
2013-10-20 21:33 程序猴 阅读(184) 评论(0) 收藏 举报http://zoomq.qiniudn.com/ZQScrapBook/ZqFLOSS/data/20070108123245/index.html
相关的另一篇文章:Python Web 服务器网关接口
http://pep-3333-wsgi.readthedocs.org/en/latest/
Introduction
WSGI是由pje在寻找web server和python web app或framework之间简单而通用的接口的结果。原先是叫python web container interface。PEP333之前的讨论稿时2003.10.8张贴到python web sig中,经过了python社区很多方面的有效讨论和修改,它逐渐演变成Web Server Gateway Interface,并最终提交成PEP 333。
那么WSGI试图要解决什么问题呢?一个python程序员面对许多web framework可供选择的时候,或者是framework中部分组件的时候,运行framework的web server是一个决定性的因素.对于一个framework作者,为所有的web server创建适配器是不可能完成的任务,如果framework作者面向WSGI API来编程,那只要web server有WSGI适配器的话,framework用户就能选择他们自己需要的web server了.
How WSGI works
PEP文档写的很好,非常容易理解,它是关于WSGI 知识最好的资料,而我们这里只是WSGI的基本知识。
WSGI规范了2个接口,一个接口是为web server与app的交互,另一个接口是为app与web server的交互。
对于web server与app交互,它是调用一个函数或一个callable对象,这些由app提供。这个函数或对象有2个位置参数,environ和start_response,environ参数必须是一个内建的python字典类型,包含有CGI形式的环境变量,比如REQUEST_METHOD,必要的WSGI变量,也包括server扩展变量。start_response则是一个python函数。
对于app与web server的交互,app准备它要发送的header信息,然后调用给定的start_response,并带有startus状态码和一个header信息列表,然后app准备响应信息的body,形式是字符串列表或是字符串迭代器,返回的响应信息被传给web server。
在接收到来自app的列表或迭代器中含有的响应信息,web server会把字符串以流形式发送给client。
来点代码吧,考虑一下下面简单的CGI脚本:
#!/usr/bin/env python
print 'Content-type: text/plain\n\n'
print 'Hello world!'
作为一个WSGI app,我们这样来编码:
(hello_world.py)
def application(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 WSGIAppClass:
"""Produce the same output, but using a class
"""
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"
现在要测试我们的代码,我们配置wsgiref模块中的SimpleHTTPServer。
from wsgiref import simple_server
import hello_world
httpd = simple_server.WSGIServer(('',8000),simple_server.WSGIRequestHandler)
httpd.set_app(hello_world.application)
httpd.serve_forever()
然后我把它部署到使用FastCGI的apache上:
from flup.server.fcgi import WSGIServer
import hello_world
WSGIServer(hello_world.application).run()
或者部署到使用ISAPI的IIS上:
import isapi_wsgi
import hello_world
# The entry points for the ISAPI extension.
def __ExtensionFactory__():
return isapi_wsgi.ISAPISimpleHandler(hello = hello_world.application)
if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters()
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="isapi-wsgi-hello",
Description = "ISAPI-WSGI Hello WOrld",
ScriptMaps = sm,
ScriptMapUpdate = "replace"
)
params.VirtualDirs = [vd]
HandleCommandLine(params)
ISAPI上的部署有些例外,在其他的WSGI webserver适配器则是相当简单的脚本。
为了更好地理解一个webserver适配器是如何实现的,pep 333文档上描述的CGI适配器是最好的起点。
WSGI Web Server Implementations
略
WSGI Enabled Frameworks
略
WSGI Middleware
在过去的6个月里,WSGI的讨论和开发已经开始聚焦于WSGI Middleware上了,也就是使用python组件,这些组件是支持WSGI app和Server API的,把这些组件用pipeline管道线连接起来创建一个web app。下面是一个简单的WSGI Authentication组件,我们用它来包装一个已经存在的WSGI app外:
class AuthenticationMiddleware:
def __init__(self, app, allowed_usernames):
self.app = app
self.allowed_usernames = allowed_usernames
def __call__(self, environ, start_response):
if environ.get('REMOTE_USER','anonymous') in self.allowed_usernames:
return self.app(environ, start_response)
start_response(
'403 Forbidden', [('Content-type', 'text/html')])
return ['You are forbidden to view this resource']
现在部署一下这个代码,就用PEP 333上的WSGI CGI适配器来运行它,
#!/usr/local/bin/python2.4
import hello_world
import middleware
allowed_users = ['guido','monty']
if __name__ == '__main__':
from cgi_wsgi import run_with_cgi
run_with_cgi(middleware.AuthenticationMiddleware(
hello_world.application,allowed_users))
(注:我还是用wsgiref的Simple HTTP Server)
WSGIUtils,Python Paste,flup,Python Web Modules这些包都提供了middleware组件,有session management,error handling,authentication,compression和URL parsing。( 还有wsgi_xlst,pyfileserver,省略)
Configuration
在创建一个大规模的python web app中使用一些框架中立的组件时,会有一些麻烦和问题。组合所有的middleware组件的代码会显得很乱,看看这个假设的代码:
def configure(app):
return ErrorHandlerMiddleware(
SessionMiddleware(
IdentificationMiddleware(
AuthenticationMiddleware(
UrlParserMiddleware(app))))))
if __name__ == '__main__':
app = Application()
app_pipeline = configure(app)
server = Server(app_pipeline)
server.serve()
一旦把需要传入的参数加入,代码的维护和调试将会变成噩梦。开发一个标准的方式来配置WSGI app已经成为Python Web SIG讨论的话题。Ian Bicking已经开发了Paste Deployment,这是一个用来查找和配置WSGI app和server的系统。要让Paste Deployment来配置加载WSGI app,需要用一个app factory来包装WSGI app。
(hello_world.py)
def application(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']
def app_factory(global_config, **local_config):
return application
配置文件(config.ini)
[app:main]
paste.app_factory = hello_world:app_factory
相应的代码是:
from paste.deploy import loadapp
wsgi_app = loadapp('config:/path/to/config.ini')
(注:这是uri,一定要用绝对路径,如我的机子上是=>d:\work\wsgi\config.ini)
运行环境的部署的WSGI代码,我们用FastCGI:
from flup.server.fcgi import WSGIServer
WSGIServer(wsgi_app).run()
(注:我还是用wsgiref的simple_server)
Paste Deploy配置文件有一些标签来定义多个application,global,local变量,filter和定义的pipeline。这个pipeline的定义是用来简化带有WSGI middleware的app的创建。把我们前面写的Authentication Middleware加入一个filter,这个是由paste deploy使用,我们需要创建一个filter factory。
(filter_auth.py)
def auth_filter_factory(global_conf, **local_conf):
if local_conf.has_key('allowed_users'):
allowed_users = local_conf['allowed_users']
def filter(app):
return AuthenticationMiddleware(app, allowed_users)
return filter
然后我们用一个配置文件pipeline把这个AuthenticationMiddleware和其他的filter串起来:
[pipeline:main]
pipeline = errorhandler session ident auth hello
[app:hello]
paste.app_factory = hello_wsgi:app_factory
[filter:auth]
paste.filter_factory = filter_auth:auth_filter_factory
allowed_users = ['guido', 'monty']
[filter:ident]
....
(注:这里的配置要根据自己的机子实际情况来调整)
如果我们向变换另一个authentication方法,配置文件只要更改为另一个authentication filter就行了。
更多的配置信息,可以看Paste Deployment文档。
Conclusions
WSGI是什么,一个gateway还是glue?我想这篇文章已经说明WSGI的两方面的特点。它既有webserver和python web app之间交互良好的API,也能把框架中立的组件glue粘合在一起。......
浙公网安备 33010602011771号