在WSGI处理期间抛出异常,WSGI应用程序的一个核心特性是链上的每个阶段都会负责过滤请求。其思想是在处理过程中尽早拒绝错误的请求。Python的异常处理使这一点易于实现。
可以定义一个提供静态内容的WSGI应用程序,如下所示:
def static_app(
environ: Dict,
start_response: SR_Func
) -> Union[Iterator[bytes], List[bytes]]:
log = environ['wsgi.errors']
try:
print(f"CWD={Path.cwd()}", file=log)
static_path = Path.cwd()/environ['PATH_INFO'][1:]
with static_path.open() as static_file:
content = static_file.read().encode("utf-8")
headers = [
("Content-Type", 'text/plain;charset="utf-8"'),
("Content-Length", str(len(content))),
]
start_response('200 OK', headers)
return [content]
except IsADirectoryError as e:
return index_app(environ, start_response)
except FileNotFoundError as e:
start_response('404 NOT FOUND', [])
return [
f"Not Found {static_path}\n{e!r}".encode("utf-8")
]
该应用程序从当前工作目录中创建了一个Path
对象,以及一个作为请求URL一部分而提供的路径元素。路径信息是WSGI环境的一部分,位于以PATH_INFO'
为键的项中。正是由于这种解析路径的方式,因此它会有一个前导的/
,我们使用environ['PATH_INFO][1:]
来丢弃该字符。
该应用程序尝试将请求的路径以文本文件的形式打开。这里有两个常见问题,会将它们作为异常来处理:
- 如果文件是一个目录,将使用另一个应用程序
index_app
来呈现目录内容; - 如果没有找到该文件,将返回一个
HTTP 404 NOT FOUND
响应。
该WSGI应用程序引发的其他任何异常都不会被捕获。应把调用该应用程序的程序设计为具有某种通用的错误响应能力。如果应用程序不处理这些异常,则会使用通用的WSGI故障响应。
上面的处理涉及严格的运算顺序。必须读取整个文件才能创建一个正确的HTTP
Content-Length
报头。
处理异常有两种方式。一种是调用另一个应用程序。如果需要向该应用程序提供额外信息,则必须使用所需的信息来更新环境。这便是为一个复杂网站构建标准化错误页面的方法。
另一种是调用start_response()
函数并返回一个错误结果,这适用于特定的本地化行为。最终的内容以字节形式呈现,这意味着必须将Python字符串正确编码,且必须向用户代理提供编码信息,甚至在错误信息repr(e)
被下载之前就应该已经正确编码。