在之前的文章中我写了有关于如何使用PasteDeploy生成WSGI的Application。在Openstack的源码中,除了 PasteDeploy外,还有一个和WSGI密切相关的工具包WebOb。这篇文章就来讲讲这个WebOb。官网在 这:http://webob.org/

简单的说,WebOb是一个用于对WSGI request环境进行包装(也就是变得易用)以及用于创建WSGI response的一个包。

1.Request
webob.Request是WebOb中的一个重要对象。其会的对WSGI的environ(就是传递给WSGI APP的那个参数)参数进行封装。
一个简单的例子:

1
2
3
4
5
from webob import Request
 
req = Request.blank('/article?id=1')
from pprint import pprint
pprint(req.environ)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@OS_DEV dev]# python webobtest.py
{'HTTP_HOST': 'localhost:80',
 'PATH_INFO': '/article',
 'QUERY_STRING': 'id=1',
 'REQUEST_METHOD': 'GET',
 'SCRIPT_NAME': '',
 'SERVER_NAME': 'localhost',
 'SERVER_PORT': '80',
 'SERVER_PROTOCOL': 'HTTP/1.0',
 'wsgi.errors': <open file '<stderr>', mode 'w' at 0x7f83c59d21e0>,
 'wsgi.input': <io.BytesIO object at 0x7f83c592b590>,
 'wsgi.multiprocess': False,
 'wsgi.multithread': False,
 'wsgi.run_once': False,
 'wsgi.url_scheme': 'http',
 'wsgi.version': (1, 0)}

既然是request,那么必然有个body,并且也有request的方法(GET?POST?DELETE?),所以文档里有这么个例子:

1
2
3
4
5
6
7
8
9
10
>>> hasattr(req.body_file, 'read')
True
>>> req.body
''
>>> req.method = 'PUT'
>>> req.body = 'test'
>>> hasattr(req.body_file, 'read')
True
>>> req.body
'test'

对request请求头部的操作如下:

1
2
3
4
5
>>> req.headers['Content-Type'] = 'application/x-www-urlencoded'
>>> sorted(req.headers.items())
[('Content-Length', '4'), ('Content-Type', 'application/x-www-urlencoded'), ('Host', 'localhost:80')]
>>> req.environ['CONTENT_TYPE']
'application/x-www-urlencoded'

对请求参数的处理如下:

1
2
3
4
5
6
7
8
9
>>> req = Request.blank('/test?check=a&check=b&name=Bob')
>>> req.GET
MultiDict([(u'check', u'a'), (u'check', u'b'), (u'name', u'Bob')])
>>> req.GET['check']
u'b'
>>> req.GET.getall('check')
[u'a', u'b']
>>> req.GET.items()
[(u'check', u'a'), (u'check', u'b'), (u'name', u'Bob')]

下面这个是比较常见的查看参数的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> req.params
NestedMultiDict([(u'check', u'a'), (u'check', u'b'), (u'name', u'Bob'), (u'name', u'Joe'), (u'email', u'joe@example.com')])
>>> req.params['name']
u'Bob'
>>> req.params.getall('name')
[u'Bob', u'Joe']
>>> for name, value in req.params.items():
...     print '%s: %r' % (name, value)
check: u'a'
check: u'b'
name: u'Bob'
name: u'Joe'
email: u'joe@example.com'

一个把request传递给WSGI应用的例子:

1
2
3
4
5
6
7
8
9
from webob import Request
 
req = Request.blank('/')
 
def wsgi_app(environ, start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
    return ['Hi!']
 
print req.call_application(wsgi_app)

输出:

1
2
[root@OS_DEV dev]# python webobtest.py
('200 OK', [('Content-type', 'text/plain')], ['Hi!'])

2.Response
webob.Response包含了标准WSGI response的所有要素。其本身也可以看成是一个WSGI的application。你可以通过req.call_application(res)对其调用。

最简单的例子如下:

1
2
3
4
5
6
7
8
>>> from webob import Response
>>> res = Response()
>>> res.status
'200 OK'
>>> res.headerlist
[('Content-Type', 'text/html; charset=UTF-8'), ('Content-Length', '0')]
>>> res.body
''

如何写入body:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> res = Response(content_type='text/plain', charset=None)
>>> f = res.body_file
>>> f.write('hey')
>>> f.write(u'test')
Traceback (most recent call last):
  . . .
TypeError: You can only write unicode to Response if charset has been set
>>> f.encoding
>>> res.charset = 'utf8'
>>> f.encoding
'utf8'
>>> f.write(u'test')
>>> res.app_iter
['', 'hey', 'test']
>>> res.body
'heytest'

注意下这个例子,这个例子把普通的WSGI的应用通过Request和Response做了一个简单的包装,虽然没有太大的修改,但对于之后使用装饰器的情况来说,是个不错的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
>>> def my_app(environ, start_response):
...     req = Request(environ)
...     res = Response()
...     res.content_type = 'text/plain'
...     parts = []
...     for name, value in sorted(req.environ.items()):
...         parts.append('%s: %r' % (name, value))
...     res.body = 'n'.join(parts)
...     return res(environ, start_response)
>>> req = Request.blank('/')
>>> res = req.get_response(my_app)
>>> print res
200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: ...
 
HTTP_HOST: 'localhost:80'
PATH_INFO: '/'
QUERY_STRING: ''
REQUEST_METHOD: 'GET'
SCRIPT_NAME: ''
SERVER_NAME: 'localhost'
SERVER_PORT: '80'
SERVER_PROTOCOL: 'HTTP/1.0'
wsgi.errors: <open file '<stderr>', mode 'w' at ...>
wsgi.input: <...IO... object at ...>
wsgi.multiprocess: False
wsgi.multithread: False
wsgi.run_once: False
wsgi.url_scheme: 'http'
wsgi.version: (1, 0)

3.Exceptions
其实就是对HTTP错误代码的一个封装。也可以看成是一个WSGI的应用。

一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
>>> from webob.exc import *
>>> exc = HTTPTemporaryRedirect(location='foo')
>>> req = Request.blank('/path/to/something')
>>> print str(req.get_response(exc)).strip()
307 Temporary Redirect
Location: http://localhost/path/to/foo
Content-Length: 126
Content-Type: text/plain; charset=UTF-8
 
307 Temporary Redirect
 
The resource has been moved to http://localhost/path/to/foo; you should be redirected automatically.

4. WSGIfy decorator
结合上面的例子,既然WebOb可以让WSGI的请求变得更加简单、强大,那么能不能不用原始的那种WSGI的参数和返回格式,而全部用WebOb替代?可以的,通过WSGIfy decorator这个装饰器。

比如这个最简单的例子:

1
2
3
@wsgify
def myfunc(req):
    return webob.Response('hey there')

调用的时候有两个选择:

1
app_iter = myfunc(environ, start_response)

或:

1
resp = myfunc(req)

第一种选择就是最原始和标准的的WSGI格式,第二种选择则是WebOb封装过后的格式。说实话后者看上去更加符合逻辑(给你个请求,给我个响应)。

如果myfanc直接返回一个Exception,那么就会的相当于直接调用Exception这个WebOb的WSGI Application,可以很容易的返回异常页面。

另外也可以对Request进行继承,修改其内容,对真正的Request做一些判断(个人感觉像是在过滤),比如:

1
2
3
4
5
6
7
8
9
10
class MyRequest(webob.Request):
    @property
    def is_local(self):
        return self.remote_addr == '127.0.0.1'
@wsgify(RequestClass=MyRequest)
def myfunc(req):
    if req.is_local:
        return Response('hi!')
    else:
        raise webob.exc.HTTPForbidden

需要记住一点:被@wsgify修饰过后的那些func,其Return的是个对Response的调用。

5.总结
总结一下WebOb。既然有人用WebOb,那它必然有它的好用的地方,好用的地方在哪里呢?我个人觉得有两个:一是兼容性好,二是使用简单。
首先先说兼容性吧。之前写过文章介绍过PasteDeploy,后者可以通过标准的配置文件生成WSGI应用。那么通过WebOb写出的WSGI应用是否
可以用在这里呢?答案是可以的,上面的装饰器的例子已经介绍了,经过装饰器装饰后的func可以通过标准的WSGI方法去调用。
然后说说使用上的感觉。简单就是好用。其把WSGI的几个参数、返回的方法都封装成了Reqeust、Response这两个对象,同时还提供了一个好用的Exception对象,就这三个对象,记起来也不难,读起来也一看就知道是啥意思,所以说用起来方便。

个人觉得最有代表的例子是这个,一目了然,使人一读就懂。最神奇的是可以通过WSGI标准对其进行调用,写的时候完全可以忘了WSGI标准是啥,同时还能写出兼容WSGI工具(比如PasteDeploy)的代码,真是不错。

1
2
3
@wsgify
def myfunc(req):
    return webob.Response('hey there')

WebOb的简单介绍的更多相关文章

  1. [原创]关于mybatis中一级缓存和二级缓存的简单介绍

    关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...

  2. 利用Python进行数据分析(7) pandas基础: Series和DataFrame的简单介绍

    一.pandas 是什么 pandas 是基于 NumPy 的一个 Python 数据分析包,主要目的是为了数据分析.它提供了大量高级的数据结构和对数据处理的方法. pandas 有两个主要的数据结构 ...

  3. 利用Python进行数据分析(4) NumPy基础: ndarray简单介绍

    一.NumPy 是什么 NumPy 是 Python 科学计算的基础包,它专为进行严格的数字处理而产生.在之前的随笔里已有更加详细的介绍,这里不再赘述. 利用 Python 进行数据分析(一)简单介绍 ...

  4. yii2的权限管理系统RBAC简单介绍

    这里有几个概念 权限: 指用户是否可以执行哪些操作,如:编辑.发布.查看回帖 角色 比如:VIP用户组, 高级会员组,中级会员组,初级会员组 VIP用户组:发帖.回帖.删帖.浏览权限 高级会员组:发帖 ...

  5. angular1.x的简单介绍(二)

    首先还是要强调一下DI,DI(Denpendency Injection)伸手获得,主要解决模块间的耦合关系.那么模块是又什么组成的呢?在我看来,模块的最小单位是类,多个类的组合就是模块.关于在根模块 ...

  6. Linux的简单介绍和常用命令的介绍

    Linux的简单介绍和常用命令的介绍 本说明以Ubuntu系统为例 Ubuntu系统的安装自行百度,或者参考http://www.cnblogs.com/CoderJYF/p/6091068.html ...

  7. iOS-iOS开发简单介绍

    概览 终于到了真正接触IOS应用程序的时刻了,之前我们花了很多时间去讨论C语言.ObjC等知识,对于很多朋友而言开发IOS第一天就想直接看到成果,看到可以运行的IOS程序.但是这里我想强调一下,前面的 ...

  8. iOS开发多线程篇—多线程简单介绍

    iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcod ...

  9. iOS开发UI篇—UITabBarController简单介绍

    iOS开发UI篇—UITabBarController简单介绍 一.简单介绍 UITabBarController和UINavigationController类似,UITabBarControlle ...

随机推荐

  1. ExtJS4.2学习(七)EditorGrid可编辑表格(转)

    鸣谢地址:http://www.shuyangyang.com.cn/jishuliangongfang/qianduanjishu/2013-11-14/176.html ------------- ...

  2. PAT-乙级-1029. 旧键盘(20)

    1029. 旧键盘(20) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 旧键盘上坏了几个键,于是在敲一段文字的 ...

  3. uva 11143

    #include<cstdio> #include<cstring> #include<algorithm> #define maxn 5100 #include& ...

  4. ural 1250

    有点坑的dfs  看懂题应该就会做了 神圣海必然围成一个圈  dfs将神圣还外围的全部去掉   简单题 #include <cstdio> #include <cstring> ...

  5. XSS 攻击在它的面前都弱爆了!

    虽然双十一刚刚过去不久,但是对很多工程师来说,连续熬夜加班的「噩梦」似乎还没有过去.尤其是像双十一这种活动,对于电商网站的工程师们来说,他们需要彻夜的加班加点来保障网站的稳定性和安全性.当然,面对上千 ...

  6. 用CodeViz绘制函数调用关系图(call graph)

    CodeViz是<Understanding The Linux Virtual Memory Manager>(at Amazon,下载地址在页尾)的作者 Mel Gorman 写的一款 ...

  7. hdu 3927 Math Geek

    纯数论题,不解释!!!! 代码如下: #include<stdio.h> int main(){ ,m; scanf("%d",&t); while(t--){ ...

  8. JS作用域与闭包--实例

    <script> "use strict" //函数作用域 function func(){ var arr = [1,3,5,7,9]; var sum = 0; f ...

  9. http_build_query函数(学习)

    http_build_query函数   http_build_query -- 生成 url-encoded 之后的请求字符串 描述 string http_build_query ( array ...

  10. 史上最全面的FRM与CFA的区别对比分析,适合新人看

    简单地自我介绍:本人于纽约完成了研究生阶段的学习后,在华尔街混迹了几年的时间,已获取FRM证书,正在积极准备CFA. 上海财经大学FRM培训中心前言导读 经常看到CFA持证人平均年收入为$XXX之类的 ...