zoukankan      html  css  js  c++  java
  • WebOb的简单介绍

    在之前的文章中我写了有关于如何使用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')
  • 相关阅读:
    http 协议相关问题
    网卡中断及多队列
    Visual Studio Code 配置C/C++环境
    C++通用框架和库
    命令行的艺术
    NetScaler Logs Collection Guide
    C++性能榨汁机之无锁编程
    Codeforces 839E Mother of Dragons【__builtin_popcount()的使用】
    C/C++中__builtin_popcount()的使用及原理
    Codeforces 839D Winter is here【数学:容斥原理】
  • 原文地址:https://www.cnblogs.com/zmlctt/p/4208915.html
Copyright © 2011-2022 走看看