WSGI应用常见的几种写法

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)

那么,类方法,可以用么,当然可以,classmethodstaticmethod在使用上可以看做只是参数有个区别而已,如下:

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/



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值