WSGI(web server gateway interface,Web服务网关接口)定义了一个相对简单且标准化的设计模式,用于创建对Web请求的响应。这是大多数基于Python的Web服务器的通用框架。更多相关信息,可访问http://wsgi.readthedocs.org/en/latest/。
关于WSGI的一些背景知识,可访问https://www.python.org/dev/peps/pep-0333。
Python库中的wsgiref
包包含了一个WSGI的参考实现。每个WSGI应用程序都具有相同的接口,如下所示:
def some_app(environ, start_response):
return content
参数environ
是一个字典,以单一且统一的结构包含了请求的所有参数。报头、请求方法、路径以及任何表单和上传文件的附件都会存放在这个环境中。除此之外,伴随WSGI请求处理的一些项目还提供了操作系统级的上下文。
参数start_response
是一个专门用于发送状态和响应报头的函数。WSGI服务器中最终负责构建响应的部分将使用给定的start_response()
函数,并构建响应文档作为返回值。
从WSGI应用程序返回的响应是字符串序列,或是会返回给用户代理的类字符串文件封装器。如果使用HTML模板工具,那么该序列可能只含有一项。在某些情况下(例如Jinja2模板)会将模板惰性渲染为文本块序列,这允许服务器交错执行模板填充和用户代理的下载。
可以对WSGI应用程序使用如下类型提示:
from typing import (
Dict, Callable, List, Tuple, Iterator, Union, Optional
)
from mypy_extensions import DefaultArg
SR_Func = Callable[
[str, List[Tuple[str, str]], DefaultArg(Tuple)], None]
def static_app(
environ: Dict,
start_response: SR_Func
) -> Union[Iterator[bytes], List[bytes]]:
类型定义SR_Func
是函数start_response
的签名。请注意,该函数有一个可选参数,要求用mypy_extensions
模块中的函数来定义该特征。
static_app()
作为整个WSGI函数,需要使用环境参数和start_response()
函数。返回的结果是字节序列,或者是基于字节的迭代器。可以扩展函数static_app()
返回类型的组合,使其包含BinaryIO
和List[BinaryIO]
,但本章的任何示例都不会用到它们。
截至本书出版时,wsgiref
包中尚没有完整的类型定义集合。具体而言,wsgiref.simple_server
模块缺少合适的存根定义(stub definition),因此mypy会发出警告。
每个WSGI应用程序都设计成了函数的集合。可以将该集合视为嵌套的函数或一条转换链。链中的每个应用程序都会返回一个错误,或者将请求转交给另一个应用程序来确定最终结果。
通常用URL路径来确定要使用的备选应用程序,这会形成一个WSGI应用程序的树形结构,并且它们可以共享公共组件。
下面是一个非常简单的路由应用程序,它获取URL路径中的第一个元素,并以此定位另一个提供内容的WSGI应用程序。
SCRIPT_MAP = {
"demo": demo_app,
"static": static_app,
"index.html": welcome_app,
}
def routing(environ, start_response):
top_level = wsgiref.util.shift_path_info(environ)
app = SCRIPT_MAP.get(top_level, welcome_app)
content = app(environ, start_response)
return content
该应用程序会使用wsgiref.util.shift_path_info()
函数来调整环境。它会对environ['PATH_INFO']
字典中请求路径中的各项进行头尾分割。第一个/
之前的路径头部会移至环境中的SCRIPT_NAME
项,PATH_INFO
项则由路径的尾部内容进行更新。返回的值也会作为路径头部,其值和environ['SCRIPT_NAME']
一致。在没有路径可解析的情况下,返回值为None
,且不对环境进行更新。
函数routing()
使用路径中的第一项来定位SCRIPT_MAP
字典中的应用程序。我们使用welcome_app
作为默认设置,避免请求的路径不符合映射规则,这似乎比返回HTTP的404 NOT FOUND
错误要好一些。
该WSGI应用程序是一个函数,用于选取其他WSGI函数。请注意,路由函数并不返回函数,而是为所选取的WSGI应用程序提供修改过的环境。这是将任务从一个函数转移到另一个函数的典型设计模式。
框架通过正则表达式来泛化路径匹配过程。可以设想使用正则表达式序列和WSGI应用程序来配置routing()
函数,而不是使用字符串到WSGI应用程序的映射。改进的routing()
函数应用程序会计算每个正则表达式来寻找匹配项。在找到匹配后,在调用所请求的应用程序之前可以使用任何match.groups()
函数来更新环境。