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等支持,这样我们可以胶合一个自己的框架。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。