WSGI学习系列WebOb

1. WSGI Server <-----> WSGI Middleware<-----> WSGI Application 

1.1 WSGI Server

  wsgi server可以理解为一个符合wsgi规范的web server,接收request请求,封装一系列环境变量,按照wsgi规范调用注册的wsgi app,最后将response返回给客户端。

1.2 WSGI Application

      wsgi application就是一个普通的callable对象,当有请求到来时,wsgi server会调用这个wsgi application。

   这个对象接收两个参数,通常为environ,start_response。

   environ可以理解为环境变量,跟一次请求相关的所有信息都保存在了这个环境变量中,包括服务器信息,客户端信息,请求信息。

   start_response是一个callback函数,wsgi application通过调用start_response,将response headers/status 返回给wsgi server。

   此外这个wsgi application会return一个iterator对象 ,这个iterator就是response body。

1.3 WSGI Middleware

  中间件是为了使应用程序拥有额外的行为而存在的。如果你不能随意决定是否将它放到程序的前面,它就不再是中间件了。而是程序的一部分。

  比如:URL dispatch功能,权限判断。

1.4 WSGI模型示例
 
from wsgiref.simple_server import make_server

URL_PATTERNS= (
    ('hi/','say_hi'),
    ('hello/','say_hello'),
    )

class Dispatcher(object):

    def _match(self,path):
        path = path.split('/')[1]
        for url,app in URL_PATTERNS:
            if path in url:
                return app

    def __call__(self,environ, start_response):
        path = environ.get('PATH_INFO','/')
        app = self._match(path)
        if app :
            app = globals()[app]
            return app(environ, start_response)
        else:
            start_response("404 NOT FOUND",[('Content-type', 'text/plain')])
            return ["Page dose not exists!"]

def say_hi(environ, start_response):
    start_response("200 OK",[('Content-type', 'text/html')])
    return ["kenshin say hi to you!"]

def say_hello(environ, start_response):
    start_response("200 OK",[('Content-type', 'text/html')])
    return ["kenshin say hello to you!"]

app = Dispatcher()

httpd = make_server('', 8000, app)
print "Serving on port 8000..."
httpd.serve_forever()

2.WebOb

  WebOb是一个用于对WSGI Request环境进行包装,以及用于创建WSGI Response的一个包。

  WebOb把WSGI的几个参数、返回的方法都封装成了Reqeust、Response这两个对象,同时还提供了一个使用方便的Exception对象。

  Related API Link: http://docs.webob.org/en/latest/reference.html

2.1 Request

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)

2.2 Response

from webob import Response
from pprint import pprint
res = Response(content_type='text/plain', charset=None) f = res.body_file f.write('hey') f.write('test') # pprint(res.status) # pprint(res.headerlist) # pprint(res.body)

2.3 Full Sample about Request and Response

from webob import Request
from webob import Response
from webob.dec import wsgify

# @wsgify
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

2.4 Exception

  下述总结了WebOb对于HTTP返回码的类定义。

Exception
  HTTPException
    HTTPOk
      * 200 - :class:`HTTPOk`
      * 201 - :class:`HTTPCreated`
      * 202 - :class:`HTTPAccepted`
      * 203 - :class:`HTTPNonAuthoritativeInformation`
      * 204 - :class:`HTTPNoContent`
      * 205 - :class:`HTTPResetContent`
      * 206 - :class:`HTTPPartialContent`
    HTTPRedirection
      * 300 - :class:`HTTPMultipleChoices`
      * 301 - :class:`HTTPMovedPermanently`
      * 302 - :class:`HTTPFound`
      * 303 - :class:`HTTPSeeOther`
      * 304 - :class:`HTTPNotModified`
      * 305 - :class:`HTTPUseProxy`
      * 307 - :class:`HTTPTemporaryRedirect`
    HTTPError
      HTTPClientError
        * 400 - :class:`HTTPBadRequest`
        * 401 - :class:`HTTPUnauthorized`
        * 402 - :class:`HTTPPaymentRequired`
        * 403 - :class:`HTTPForbidden`
        * 404 - :class:`HTTPNotFound`
        * 405 - :class:`HTTPMethodNotAllowed`
        * 406 - :class:`HTTPNotAcceptable`
        * 407 - :class:`HTTPProxyAuthenticationRequired`
        * 408 - :class:`HTTPRequestTimeout`
        * 409 - :class:`HTTPConflict`
        * 410 - :class:`HTTPGone`
        * 411 - :class:`HTTPLengthRequired`
        * 412 - :class:`HTTPPreconditionFailed`
        * 413 - :class:`HTTPRequestEntityTooLarge`
        * 414 - :class:`HTTPRequestURITooLong`
        * 415 - :class:`HTTPUnsupportedMediaType`
        * 416 - :class:`HTTPRequestRangeNotSatisfiable`
        * 417 - :class:`HTTPExpectationFailed`
        * 422 - :class:`HTTPUnprocessableEntity`
        * 423 - :class:`HTTPLocked`
        * 424 - :class:`HTTPFailedDependency`
        * 428 - :class:`HTTPPreconditionRequired`
        * 429 - :class:`HTTPTooManyRequests`
        * 431 - :class:`HTTPRequestHeaderFieldsTooLarge`
        * 451 - :class:`HTTPUnavailableForLegalReasons`
      HTTPServerError
        * 500 - :class:`HTTPInternalServerError`
        * 501 - :class:`HTTPNotImplemented`
        * 502 - :class:`HTTPBadGateway`
        * 503 - :class:`HTTPServiceUnavailable`
        * 504 - :class:`HTTPGatewayTimeout`
        * 505 - :class:`HTTPVersionNotSupported`
        * 511 - :class:`HTTPNetworkAuthenticationRequired`