您的位置:首页 > 其它

WSGI应用常见的几种写法

2013-04-03 11:15 323 查看
Python WSGI标准PEP-0333中说到一个WSGI是一个可调用callable的对象,这包含了一个函数,一个方法,一个类,或者拥有call方法的实例。下面我就对这句话做一个总结,举几个例子。这里的WSGI应用的功能都是返回请求的环境信息environ。


1 服务端

采用简单的WSGI服务器参考实现,如下:
if __name__ == '__main__':
from wsgiref.simple_server import make_server
server = make_server('192.168.1.11', 7070, wsgi_application)
server.serve_forever()


2 函数方法

这是最简单的方法,先调用
start_response()
处理返回头信息,函数再返回可迭代对象,比如列表或者字符串。
def app1(environ,start_response):
start_response("200 OK",[("Content-type","text/plain")])
content = []
for k,v in environ.iteritems():
content.append('%s:%s \n' % (k,v))
return content#返回的列表或者字符串


相应的服务端:
server = make_server('192.168.1.11', 7070, app1)


3 带有call方法的实例

类定义中实现call方法,WSGI应用是这个类的一个实例。
class app2(object):
def __init__(self):
pass
def __call__(self,environ,start_response):
start_response("200 OK",[("Content-type","text/plain")])
content = []
for k,v in environ.iteritems():
content.append('%s:%s \n' % (k,v))
return content#返回的列表或者字符串

application = app2()


服务端:
server = make_server('192.168.1.11', 7070, application)


4 类class

用类作为WSGI应用不太一样,调用这个类时会产生一个类的实例,这个实例随后会需要iter迭代返回值。
class app3(object):
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)
content = ''
for k,v in self.environ.iteritems():
content += '%s:%s \n' % (k,v)
yield content#返回的是字符串


服务端:
server = make_server('192.168.1.11', 7070, app3)


这里注意,和上面的比较可以看出,如果需要把类的实例作为WSGI应用,则类中需要实现call方法,并且作为WSGI应用的应该是这个类的实例。


5 方法method

用一个方法来作为WSGI应用,那么这个方法不可能是实例方法(上面已经讲过),一种方式肯定就是类中的静态方法了,类中的静态方法,就当做一个全局函数一样理解吧。
class app4(object):
def __init__(self):
pass
@staticmethod
def wsgi(environ,start_response):
start_response("200 OK",[("Content-type","text/plain")])
content = []
for k,v in environ.iteritems():
content.append('%s:%s \n' % (k,v))
return content#返回的是列表或者字符串


服务端:
server = make_server('192.168.1.11', 7070, app4.wsgi)


那么,类方法,可以用么,当然可以,
classmethod
staticmethod
在使用上可以看做只是参数有个区别而已,如下:
class app5(object):
def __init__(self):
print 'This is app5'
pass
@classmethod
def wsgi(cls,environ,start_response):
start_response("200 OK",[("Content-type","text/plain")])
content = []
for k,v in environ.iteritems():
content.append('%s:%s \n' % (k,v))
return content #返回列表或者字符串


服务端:
server = make_server('192.168.1.11', 7070, app5.wsgi)


上面中说到了WSGI应用的几种基本形式,这一节就来说一些高级的用法,所谓高级,我理解的也就是使用装饰器
@
,WebOb,以及中间件的形式,当然,还有一些更有意思的用法,比如Paste.Deploy这种载入WSGI应用的形式,我以后会做总结。还是和前篇WSGI应用常见的几种写法-基本形式一样,本文实现的WSGI应用都是一个回显请求环境信息的程序。


1 WebOb

WebOb就是将请求和相应封装成Request和Response类,就可以使用一个类方法简化真个操作,如下,直接将一个字符串作为resp_body传给给Response对象,而不用考虑WSGI中的可迭代对象,这里要注意,Response对象
是一个WSGI应用
,所以在最后的时候我们使用
resp(environ,start_response)
就返回了,我们不关心返回的细节问题了,而在标准WSGI中,这一步才只是WSGI应用的入口函数。
def app6(environ, start_response):
req = Request(environ)
resp_body = ''
for k,v in req.environ.iteritems():
resp_body += '%s:%s\n' % (k,v)
resp = Response(body=resp_body)
return resp(environ, start_response)


服务端:
server = make_server('192.168.1.11', 7070, app6)


2 用装饰器@,Controller

用装饰器@以后,重要的思想就是把WSGI应用关于返回流程的处理(包括header,状态等)和返回Body的处理分开,这样的话实际上一个应用只关注于产生返回的Body就可,而其他的处理流程则交给装饰器controller来完成,这个controller,不要理解成控制神马的,只是一个名字,其原意是作为WSGI中
一个资源的控制
,不用太操心就当做一个名字就好。
def controller1(func):
def application(environ,start_response):
#do something else
resp_body = func(environ)
start_response("200 OK",[("Content-type","text/plain")])
return resp_body
return application

@controller1
def app7(environ):
content = []
for k,v in environ.iteritems():
content.append('%s:%s \n' % (k,v))
return content


服务端:
server = make_server('192.168.1.11', 7070, app7)


3 结合WebOb和装饰器

上面两种方式可能看起来没多大用,但是二者结合到一起,那就不一般了,这样子的话我们用装饰器controller来处理environ,并封装Request和Response,最后让应用函数来处理返回信息,只给出一个字符串Body就可以了。这会大大简化WSGI应用的开发流程。
def controller2(func):
def replacement(environ, start_response):
req = Request(environ)
try:
resp_body = func(req)
except exc.HTTPException, e:
resp_body = e
resp = Response(body=resp_body)#body must be a string
resp.status = '200 very OK'
return resp(environ, start_response)
return replacement

@controller2
def app8(req):
ret = ''
for k,v in req.environ.iteritems():
ret = ret + '%s:%s \n' % (k,v)
return ret


服务端:
server = make_server('192.168.1.11', 7070, app8)


以上是用装饰器来装饰一个函数,还有一种用法,用装饰器来装饰一个类。
def controller3(cls):
def replacement(environ, start_response):
req = Request(environ)
instance = cls(req, **req.urlvars)
method = req.method
if method == 'GET':
func = getattr(instance, method.lower())
resp_body = func()
if isinstance(resp_body, basestring):
resp = Response(body=resp_body)
return resp(environ,start_response)
return replacement

class App9(object):
def __init__(self,req,**args):
self.req = req
def get(self):
body = ''
for k,v in self.req.environ.iteritems():
body += '%s"%s \n' % (k,v)
return body

app9 = controller3(App9)


上面关于类的装饰器可以仔细琢磨一下哈,相应的服务端:
server = make_server('192.168.1.11', 7070, app9)


4 总结

两篇文章我一共说明了9种WSGI应用的写法,当然WSGI只是一个协议,相应的实现还是很灵活的,以后再实践中再分享一些新的用法。整个代码我使用Gist存放,连接在此:https://gist.github.com/4378216

原创文章,转载请注明: 转载自Wang
Chang's Blog

本文链接地址: http://blog.wachang.net/2013/03/wsgi-application-style-adv/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: