Python中的WSGI

https://jasonlvhit.github.io/...

简单来看,wsgi是web组件的接口规范,在wsgi规范里,wsgi把web组件分成三个部分,wsgi server,wsgi middleware,wsgi application

application

更多的时候,我们关心的只是wsgi application, wsgi application就是一个Python中的callable对象(函数,或定义了__call__方法的类等), 接受两个参数,environ和start_response,参数只是一个名字而已,environ指的是环境变量,start_response是一个回调函数。在实际创建一个运行的app时,一个参数都不需要我们传,无论是environ还是回调函数都由服务器,也就是wsgi server负责。

environ

environ 包括 服务器环境变量,客户端环境变量,和请求中的数据method,path等等。environ是怎么生成的呢?

wsgi server主要分为两部分,server和handler。server监听端口,handler处理请求。server接受到一个request,解析客户端environ,bind一个handler,handler解析请求,将请求信息放入environ,最后服务器端环境变量也放入environ中。这就是environ的全部了。

start_response

在wsgiref中,回调函数start_response的源码是下面这样的:

def start_response(self, status, headers,exc_info=None):

    """'start_response()' callable as specified by PEP 333"""

    if exc_info:
        try:
            if self.headers_sent:
                # Re-raise original exception if headers sent
                raise exc_info[0], exc_info[1], exc_info[2]
        finally:
            exc_info = None        # avoid dangling circular ref
    elif self.headers is not None:
        raise AssertionError("Headers already set!")

    assert type(status) is StringType,"Status must be a string"
    assert len(status)>=4,"Status must be at least 4 characters"
    assert int(status[:3]),"Status message must begin w/3-digit code"
    assert status[3]==" ", "Status message must have a space after code"
    if __debug__:
        for name,val in headers:
            assert type(name) is StringType,"Header names must be strings"
            assert type(val) is StringType,"Header values must be strings"
            assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
    self.status = status
    self.headers = self.headers_class(headers)
    return self.write

函数接受两个参数,status,也就是HTTP状态码,和headers,请求头。函数验证这两个信息,并将其塞入服务器。最后调用write方法,write方法的功能是“to buffer data for send to client”,也就是把信息再塞回客户端。

说到这,我们就可以看看一个在PEP3333中给出的简单的wsgi app了。

HELLO_WORLD = b"Hello world!n"

def simple_app(environ, start_response):
    """Simplest possible application object"""
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return [HELLO_WORLD]

如上所述,两个参数,environ, start_response, 回调函数start_response传入status和headers,最后返回一个字符串,也就是服务器返回给客户端的核心数据。

同样,我们说过,一个wsgi application是一个callable对象,只要他是callable(实现__call__方法)的就可以。在PEP3333中也给出了一个简单的类形式的app:

class AppClass:

def __call__(self, environ, start_response):
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
return [HELLO_WORLD]

到这,其实wsgi的结构很清晰了,最后,还差一个wsgi middleware。

middleware

按照PEP3333的说法,wsgi server是server端的东西,wsgi app是应用端的东西,并不是说middleware是中间的东西,middleware是一个两头兼顾的东西。

我们可以把app看成一个蛋糕,middleware则是这块蛋糕的包装。web服务中,例如session,route,auth等等,都是用middleware实现的。

例如一个PEP3333中的这个转换piglatin的wsgi middleware:

from piglatin import piglatin

class LatinIter:

"""Transform iterated output to piglatin, if it's okay to do so

Note that the "okayness" can change until the application yields
its first non-empty bytestring, so 'transform_ok' has to be a mutable
truth value.
"""

def __init__(self, result, transform_ok):
    if hasattr(result, 'close'):
        self.close = result.close
    self._next = iter(result).__next__
    self.transform_ok = transform_ok

def __iter__(self):
    return self

def __next__(self):
    if self.transform_ok:
        return piglatin(self._next())   # call must be byte-safe on Py3
    else:
        return self._next()

class Latinator:

# by default, don't transform output
transform = False

def __init__(self, application):
    self.application = application

def __call__(self, environ, start_response):

    transform_ok = []

    def start_latin(status, response_headers, exc_info=None):

        # Reset ok flag, in case this is a repeat call
        del transform_ok[:]

        for name, value in response_headers:
            if name.lower() == 'content-type' and value == 'text/plain':
                transform_ok.append(True)
                # Strip content-length if present, else it'll be wrong
                response_headers = [(name, value)
                    for name, value in response_headers
                        if name.lower() != 'content-length'
                ]
                break

        write = start_response(status, response_headers, exc_info)

        if transform_ok:
            def write_latin(data):
                write(piglatin(data))   # call must be byte-safe on Py3
            return write_latin
        else:
            return write

    return LatinIter(self.application(environ, start_latin), transform_ok)

middleware与函数装饰器很像。

最后,一个应用的middleware不止一层,middleware可能像下面一样工作:

def configure(app):
   return ErrorHandlerMiddleware(
           SessionMiddleware(
            IdentificationMiddleware(
             AuthenticationMiddleware(
              UrlParserMiddleware(app))))))

webframework

最后我们来看webframework,现在回想一下包括Django,Flask等框架,我们可以察觉出Python的web框架并不是特别的难以搭建。在4年前,第一个版本的Flask,只有短短600行代码,并且其中充斥着大量的注释(文档)。

一般来说,一个python的webframework需要一个wsgi工具集(werkzeug, webob(pylons)),一个模板渲染引擎(mako,jinja2)和一个ORM数据库工具集(sqlalchemy),还有其他的包括dispatcher,mail,json等支持,这样我们可以胶合一个自己的框架。


FIZLIN
514 声望8 粉丝

跟我走吧,天亮就出发


« 上一篇
asyncio Protocol