WebOb和通用标准实现WSGI框架的比较

http://tumblr.wachang.net/post/38298360375/webob-wsgi-framework-diff


作者都是Ian Bicking,两个框架分别是遵循WSGI标准的WSGI框架(以下以1方法表示)以及使用Webob来实现WSGI框架(以下一2方法表示),两篇文章的地址分别是:http://pythonpaste.org/do-it-yourself-framework.htmlhttp://docs.webob.org/en/latest/do-it-yourself.html,这里对两个文章做一个总结比较。

基本概念

controller:就是WSGI应用,2方法中格式为module_name:function_name routes:

webob是一个创建请求和回复对象的库,webob对于请求和回应的封装能力,提供了一种简单测试WSGI应用的方法,如下:

>>> from webob import Request
>>> req = Request.blank('http://localhost/test')
>>> resp = req.get_response(application)
>>> print resp
200 OK
Content-Type: text/html

Hello World!

请求部分

在普通的WSGI中,请求的信息是environ,这是一个类似CGI的字典形式。而使用webob,需要创建一个request对象,这是对environ的一个包装。如下:

from webob import Request
def app(environ, start_response):
    print 'This is environ info',environ['HTTP_HOST']
    start_response('200 OK', [('content-type', 'text/html')])
    req = Request(environ)
    print 'This is Request info',req.environ['HTTP_HOST']
    return ['Hello world!']

root@Node1:~/python# python wsgi.py 
serving on http://192.168.1.11:8080
This is environ info 192.168.1.11:8080
This is Request info 192.168.1.11:8080

关于返回

通常是函数中先调用start_response,然后函数返回可迭代对象,webob中直接构造Response对象,说白了,Response对象就是一个WSGI应用,如下:

from webob import Request
from webob import Response
def app(environ,start_response):
    resp = Response(body='Hello World!')
    resp.content_type='text/html'
    print resp
    return resp(environ,start_response)

httpserver.serve(app, host='192.168.1.11', port='8080')

root@Node1:~/python# python wsgi.py 
serving on http://192.168.1.11:8080
200 OK
Content-Length: 12
Content-Type: text/html; charset=UTF-8

关于WSGI服务器

官方有两个参考,都可以:

from paste import httpserver
    httpserver.serve(app, host='127.0.0.1', port=8080)

from wsgiref.simple_server import make_server
    server = make_server('127.0.0.1', 8080, app)
    server.serve_forever()

Webob实现的WSGI框架

所谓的routes,router就是根据HTTP请求的PATH的层次调度到不同的WSGI应用上面去。2中使用了Router这个类来实现,如下:

app = Router() app.add_route(‘/’, controller=’controllers:index’) app.add_route(‘/post’, controller=’controllers:post’)

有了router以后,我们就要看看如何载入这个controller了,根据controller的格式,我们需要载入一个模块,然后执行函数,所以写了一个此功能的函数:

import sys
def load_controller(string):
   module_name, func_name = string.split(':', 1) #分割出module和func的名字
   __import__(module_name) #buildin函数,载入模块
   module = sys.modules[module_name] #import的返回不好处理,所以这里返回Model名字
   func = getattr(module, func_name)
   return func #返回函数对象

Router有add_route方法可以加入路由香,并且Router实例有call方法,着同样,ROuter实例就可以当做一个WSGI应用来使用了。所以当一个请求到来的时候,它会根据PATH_INFO(req.path_info)作为匹配,并传递到相应的controller(WSGI应用),Router的代码如下:

from webob import Request
from webob import exc
class Router(object):
    def __init__(self):
       self.routes = [] #里面是元组,每个元组包含了匹配规则,相应的应用

    def add_route(self, template, controller, **vars):
         if isinstance(controller, basestring):
                controller = load_controller(controller)
         self.routes.append((re.compile(template_to_regex(template)),
                              controller,
                               vars))

    def __call__(self, environ, start_response):
       req = Request(environ)
       for regex, controller, vars in self.routes:
            match = regex.match(req.path_info)
            if match:
                req.urlvars = match.groupdict()
                req.urlvars.update(vars)
                return controller(environ, start_response)
        return exc.HTTPNotFound()(environ, start_response)

我们详细看看这个函数:

  • self.routes = [],是一个匹配表,表中内容为(regex, controller, vars)

  • add_route会判断controller应用是字符串或者是对象,都可以处理,如果是对象,需要实现call方法。

  • __call__方法使你可以像函数一样调用一个对象。

  • 对于请求,产生了一个request object对象,controller可以选择以request对象作为参数(在最后返回response(environ,start_response)),或者直接处理(environ,start_response)参数。

  • req.urlvars变量实际上是environ[‘wsgiorg.routing_args’]的一个映射,environ[‘wsgiorg.routing_args’]是经过match以后,WSGI应用对请求信息的修改,加入了这个wsgiorg.routing_args,值就为匹配的一些参数。

  • webob.exc.HTTPNotFound()是一个 WSGI application 用于返回404回应(注意还是要以environ和start_response参数调用).也可以加入自定义信息webob.exc.HTTPNotFound(‘No route matched’)(environ,start_response)

基本流程清楚以后,就是来看看controller端了,controller就是一个WSGI应用,但是为了简单的写应用,一般框架都会提供一个装饰器(把一个函数装饰warp成另外一个函数),利用这个装饰器,可以简化controller的开发,如下一个装饰器:

from webob import Request, Response
from webob import exc
def controller(func):  #func是自己写的应用
    def replacement(environ, start_response):
        req = Request(environ) #首先封装environ环境
        try:
             resp = func(req, **req.urlvars) #将请求和附加参数传给应用处理。返回resp是一个字符串或者一个Response对象。
        except exc.HTTPException, e:
             resp = e
        if isinstance(resp, basestring):#如果应用返回一个字符串,那么就封装为Response对象
             resp = Response(body=resp)
        return resp(environ, start_response)#Response对象是一个WSGI应用,如此调用的话就成功返回。返回的是自己,webob特色!
    return replacement#函数定义中调用另外一个函数,用这种方式。

经过如上装饰以后,自己写的WSGI应用就只需要两个参数controller_func(req, **urlvars)了,确实简化了,不用考虑一直保持environ,start_response的传递了。然后这个装饰器就可以如下使用:

@controller
def index(req):
    return 'This is the index'

再来一个复杂一点的:

@controller
def hello(req):
    if req.method == 'POST':
        return 'Hello %s!' % req.params['name']
    elif req.method == 'GET':
        return '''<form method="POST">
            You're name: <input type="text" name="name">
            <input type="submit">
            </form>'''

hello_world = Router()
hello_world.add_route('/', controller=hello)

上面一个WSGI应用实际上是一个函数,前面说到,一个WSGI应用也可以是一个类。这样的话,在写controller装饰器的时候,就要注意一点用法:

def rest_controller(cls):
    def replacement(environ, start_response):
        req = Request(environ)
        try:
             instance = cls(req, **req.urlvars)
             method = getattr(instance, action)
             resp = method()
             resp = Response(body=resp)
             return resp(environ, start_response)
    return replacement

action是req中的方法,method是类中的的方法,method()就是一个相应的执行。

class Hello(object):
    def __init__(self, req):
        self.request = req
    def get(self):
        return '''<form method="POST">
            You're name: <input type="text" name="name">
            <input type="submit">
            </form>'''
    def post(self):
        return 'Hello %s!' % self.request.params['name']

hello = rest_controller(Hello)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值