Skip to main content

21.4. wsgiref — WSGI实用程序和参考实现


Web服务器网关接口(WSGI)是Web服务器软件和用Python编写的Web应用程序之间的标准接口。拥有标准接口,可以很容易地使用支持WSGI的应用程序与多个不同的Web服务器。

只有Web服务器和编程框架的作者需要知道WSGI设计的每个细节和角落情况。您不需要了解WSGI的每个细节,只是为了安装WSGI应用程序或使用现有框架编写Web应用程序。

wsgiref 是WSGI规范的参考实现,可用于向Web服务器或框架添加WSGI支持。它提供了用于操作WSGI环境变量和响应头的实用程序,用于实现WSGI服务器的基类,用于服务WSGI应用程序的演示HTTP服务器以及用于检查WSGI服务器和应用程序以符合WSGI规范(PEP 3333)的验证工具。

有关WSGI的更多信息,以及教程和其他资源的链接,请参阅 https://wsgi.readthedocs.org/

21.4.1. wsgiref.util - WSGI环境实用程序

此模块提供了用于使用WSGI环境的各种实用程序功能。 WSGI环境是包含如 PEP 3333 中所述的HTTP请求变量的字典。所有采用 environ 参数的函数都需要提供符合WSGI的字典;请参阅 PEP 3333 了解详细规格。

wsgiref.util.guess_scheme(environ)

通过检查 environ 字典中的 HTTPS 环境变量来返回 wsgi.url_scheme 是“http”还是“https”的猜测。返回值是一个字符串。

当创建包含CGI或类似CGI的协议(如FastCGI)的网关时,此功能非常有用。通常,提供这种协议的服务器将包括当经由SSL接收到请求时具有值“1”“是”或“开”的 HTTPS 变量。因此,如果找到这样的值,此函数返回“https”,否则返回“http”。

wsgiref.util.request_uri(environ, include_query=True)

使用在 PEP 3333 的“URL重建”部分中找到的算法返回完整的请求URI,可选地包括查询字符串。如果 include_query 为false,则查询字符串不包括在生成的URI中。

wsgiref.util.application_uri(environ)

request_uri() 类似,除了忽略 PATH_INFOQUERY_STRING 变量。结果是由请求寻址的应用程序对象的基本URI。

wsgiref.util.shift_path_info(environ)

将单个名称从 PATH_INFO 移到 SCRIPT_NAME 并返回名称。 environ 字典是 modified 在位;如果您需要保持原始 PATH_INFOSCRIPT_NAME 完整,请使用副本。

如果在 PATH_INFO 中没有剩余路径段,则返回 None

通常,此例程用于处理请求URI路径的每个部分,例如将路径视为一系列字典键。此例程修改传入的环境以使其适合于调用位于目标URI的另一WSGI应用程序。例如,如果在 /foo 处有WSGI应用程序,并且请求URI路径是 /foo/bar/baz,并且 /foo 处的WSGI应用程序调用 shift_path_info(),则它将接收字符串“bar”,并且环境将被更新为适合传递在 /foo/bar 的WSGI应用程序。也就是说,SCRIPT_NAME 将从 /foo 改变为 /foo/bar,并且 PATH_INFO 将从 /bar/baz 改变为 /baz

PATH_INFO 只是一个“/”时,此例程返回一个空字符串,并将尾部斜线附加到 SCRIPT_NAME,即使空路径段通常被忽略,并且 SCRIPT_NAME 通常不以斜杠结尾。这是有意的行为,以确保应用程序可以在使用此例程执行对象遍历时,告知以 /x 结尾的URI与以 /x/ 结尾的URI之间的差异。

wsgiref.util.setup_testing_defaults(environ)

更新 environ 与测试目的的微不足道的默认值。

该例程添加WSGI所需的各种参数,包括 HTTP_HOSTSERVER_NAMESERVER_PORTREQUEST_METHODSCRIPT_NAMEPATH_INFO 和所有 PEP 3333 I-定义的 wsgi.* 变量。它仅提供默认值,不会替换这些变量的任何现有设置。

此例程旨在使WSGI服务器和应用程序的单元测试更容易设置虚拟环境。它不应该被实际的WSGI服务器或应用程序使用,因为数据是假的!

用法示例:

from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server

# A relatively simple WSGI application. It's going to print out the
# environment dictionary after being updated by setup_testing_defaults
def simple_app(environ, start_response):
    setup_testing_defaults(environ)

    status = '200 OK'
    headers = [('Content-type', 'text/plain; charset=utf-8')]

    start_response(status, headers)

    ret = [("%s: %s\n" % (key, value)).encode("utf-8")
           for key, value in environ.items()]
    return ret

with make_server('', 8000, simple_app) as httpd:
    print("Serving on port 8000...")
    httpd.serve_forever()

除了上面的环境函数,wsgiref.util 模块还提供了这些杂项实用程序:

wsgiref.util.is_hop_by_hop(header_name)

如果’header_name’是由 RFC 2616 定义的HTTP/1.1“Hop-by-Hop”报头,则返回true。

class wsgiref.util.FileWrapper(filelike, blksize=8192)

将文件状对象转换为 iterator 的包装器。生成的对象支持 __getitem__()__iter__() 迭代样式,以兼容Python 2.1和Jython。当对象被迭代时,可选的 blksize 参数将被重复地传递给 filelike 对象的 read() 方法以获得要产生的字符串。当 read() 返回一个空的字节时,迭代结束并且不可恢复。

如果 filelike 有一个 close() 方法,返回的对象也将有一个 close() 方法,它将调用 filelike 对象的 close() 方法。

用法示例:

from io import StringIO
from wsgiref.util import FileWrapper

# We're using a StringIO-buffer for as the file-like object
filelike = StringIO("This is an example file-like object"*10)
wrapper = FileWrapper(filelike, blksize=5)

for chunk in wrapper:
    print(chunk)

21.4.2. wsgiref.headers - WSGI响应头工具

这个模块提供了一个单独的类 Headers,使用类似映射的接口方便地处理WSGI响应头。

class wsgiref.headers.Headers([headers])

创建一个映射式对象包装 headers,它必须是 PEP 3333 中描述的头名称/值元组的列表。 headers 的默认值是一个空列表。

Headers 对象支持典型的映射操作,包括 __getitem__()get()__setitem__()setdefault()__delitem__()__contains__()。对于这些方法中的每一个,键是标题名称(不区分大小写),值是与该标题名称相关联的第一个值。设置标头会删除该标头的任何现有值,然后在包装标头列表的末尾添加一个新值。通常保持标题的现有顺序,将新标题添加到包装列表的末尾。

与字典不同,当您尝试获取或删除不在包装标题列表中的键时,Headers 对象不会引发错误。获取不存在的头只返回 None,删除不存在的头什么也不做。

Headers 对象也支持 keys()values()items() 方法。如果存在多值标头,则由 keys()items() 返回的列表可以包括相同的键多于一次。 Headers 对象的 len() 与其 items() 的长度相同,其与包装的头部列表的长度相同。实际上,items() 方法只返回包装的标题列表的副本。

Headers 对象上调用 bytes() 返回适合作为HTTP响应头传输的格式化的字节。每个标题放置在一个具有其值的行上,用冒号和空格分隔。每一行由回车和换行符结束,并且字节以空行终止。

除了它们的映射接口和格式化特征之外,Headers 对象还具有用于查询和添加多值标头以及用于添加具有MIME参数的标头的以下方法:

get_all(name)

返回命名标头的所有值的列表。

返回的列表将按照它们在原始标题列表中出现或添加到此实例的顺序排序,并且可能包含重复项。删除和重新插入的任何字段始终附加到标题列表。如果没有具有给定名称的字段,则返回一个空列表。

add_header(name, value, **_params)

添加(可能是多值)标头,通过关键字参数指定可选的MIME参数。

name 是要添加的头字段。关键字参数可用于为标题字段设置MIME参数。每个参数必须是字符串或 None。参数名称中的下划线转换为破折号,因为破折号在Python标识符中是非法的,但许多MIME参数名称包括破折号。如果参数值是字符串,则将其添加到报头值参数中,形式为 name="value"。如果是 None,则只添加参数名称。 (这用于没有值的MIME参数。)用法示例:

h.add_header('content-disposition', 'attachment', filename='bud.gif')

上面将添加一个看起来像这样的标题:

Content-Disposition: attachment; filename="bud.gif"

在 3.5 版更改: headers 参数是可选的。

21.4.3. wsgiref.simple_server - 一个简单的WSGI HTTP服务器

该模块实现了一个为WSGI应用程序提供服务的简单HTTP服务器(基于 http.server)。每个服务器实例在给定主机和端口上为单个WSGI应用程序提供服务。如果要在单个主机和端口上提供多个应用程序,您应该创建一个WSGI应用程序,该应用程序解析 PATH_INFO 以选择要为每个请求调用的应用程序。 (例如,使用来自 wsgiref.utilshift_path_info() 函数)。

wsgiref.simple_server.make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler)

创建一个新的WSGI服务器侦听 hostport,接受 app 的连接。返回值是所提供的 server_class 的实例,并且将使用指定的 handler_class 处理请求。 app 必须是由 PEP 3333 定义的WSGI应用程序对象。

用法示例:

from wsgiref.simple_server import make_server, demo_app

with make_server('', 8000, demo_app) as httpd:
    print("Serving HTTP on port 8000...")

    # Respond to requests until process is killed
    httpd.serve_forever()

    # Alternative: serve one request, then exit
    httpd.handle_request()
wsgiref.simple_server.demo_app(environ, start_response)

此函数是一个小但完整的WSGI应用程序,返回一个包含消息“Hello world!”的文本页面。以及 environ 参数中提供的键/值对的列表。它对于验证WSGI服务器(如 wsgiref.simple_server)是否能够正确运行简单的WSGI应用程序很有用。

class wsgiref.simple_server.WSGIServer(server_address, RequestHandlerClass)

创建 WSGIServer 实例。 server_address 应该是 (host,port) 元组,RequestHandlerClass 应该是将用于处理请求的 http.server.BaseHTTPRequestHandler 的子类。

通常不需要调用此构造函数,因为 make_server() 函数可以处理所有详细信息。

WSGIServerhttp.server.HTTPServer 的子类,所以它的所有方法(如 serve_forever()handle_request())都可用。 WSGIServer 还提供这些WSGI特定的方法:

set_app(application)

将可调用 application 设置为将接收请求的WSGI应用程序。

get_app()

返回当前设置的应用程序可调用项。

然而,通常,您不需要使用这些附加方法,因为 set_app() 通常由 make_server() 调用,并且 get_app() 主要为了请求处理程序实例的好处而存在。

class wsgiref.simple_server.WSGIRequestHandler(request, client_address, server)

为给定的 request (即套接字),client_address(host,port) 元组)和 serverWSGIServer 实例)创建HTTP处理程序。

您不需要直接创建此类的实例;它们会根据 WSGIServer 对象的需要自动创建。但是,您可以将此类作为子类,并将其作为 handler_class 提供给 make_server() 函数。一些可能相关的重写子类中的方法:

get_environ()

返回包含请求的WSGI环境的字典。默认实现复制 WSGIServer 对象的 base_environ 字典属性的内容,然后添加从HTTP请求中派生的各种标头。每个对这个方法的调用都应该返回一个包含 PEP 3333 中指定的所有相关CGI环境变量的新字典。

get_stderr()

返回应用作 wsgi.errors 流的对象。默认实现只返回 sys.stderr

handle()

处理HTTP请求。默认实现使用 wsgiref.handlers 类创建一个处理程序实例来实现实际的WSGI应用程序接口。

21.4.4. wsgiref.validate — WSGI一致性检查器

当创建新的WSGI应用程序对象,框架,服务器或中间件时,使用 wsgiref.validate 验证新代码的一致性可能很有用。此模块提供了一个函数,用于创建验证WSGI服务器或网关与WSGI应用程序对象之间的通信的WSGI应用程序对象,以检查协议一致性的双方。

请注意,此实用程序不保证完全符合 PEP 3333;该模块没有错误并不一定意味着错误不存在。但是,如果此模块确实产生错误,则几乎确定服务器或应用程序不是100%兼容。

这个模块基于Ian Bicking的“Python Paste”库中的 paste.lint 模块。

wsgiref.validate.validator(application)

包装 application 并返回一个新的WSGI应用程序对象。返回的应用程序将所有请求转发到原始 application,并且将检查 application 和调用它的服务器是否符合WSGI规范和RFC 2616。

任何检测到的不合格导致 AssertionError 升高;请注意,如何处理这些错误是服务器相关的。例如,wsgiref.simple_server 和其他基于 wsgiref.handlers 的服务器(不覆盖错误处理方法来执行其他操作)将只输出一条错误已发生的消息,并将回溯转储到 sys.stderr 或某些其他错误流。

该包装器还可以使用 warnings 模块生成输出,以指示有问题但可能实际上未被 PEP 3333 禁止的行为。除非使用Python命令行选项或 warnings API抑制它们,否则任何此类警告都将写入 sys.stderrnot wsgi.errors,除非它们恰好是同一个对象)。

用法示例:

from wsgiref.validate import validator
from wsgiref.simple_server import make_server

# Our callable object which is intentionally not compliant to the
# standard, so the validator is going to break
def simple_app(environ, start_response):
    status = '200 OK'  # HTTP Status
    headers = [('Content-type', 'text/plain')]  # HTTP Headers
    start_response(status, headers)

    # This is going to break because we need to return a list, and
    # the validator is going to inform us
    return b"Hello World"

# This is the application wrapped in a validator
validator_app = validator(simple_app)

with make_server('', 8000, validator_app) as httpd:
    print("Listening on port 8000....")
    httpd.serve_forever()

21.4.5. wsgiref.handlers - 服务器/网关基类

此模块提供用于实现WSGI服务器和网关的基本处理程序类。这些基类处理与WSGI应用程序通信的大部分工作,只要它们给出类似CGI的环境以及输入,输出和错误流。

class wsgiref.handlers.CGIHandler

通过 sys.stdinsys.stdoutsys.stderros.environ 的基于CGI的调用。当您有一个WSGI应用程序并想要将其作为CGI脚本运行时,这是有用的。只需调用 CGIHandler().run(app),其中 app 是您要调用的WSGI应用程序对象。

此类是 BaseCGIHandler 的子类,将 wsgi.run_once 设置为true,将 wsgi.multithread 设置为false,将 wsgi.multiprocess 设置为true,并始终使用 sysos 来获取必要的CGI流和环境。

class wsgiref.handlers.IISCGIHandler

CGIHandler 的一种特殊替代方法,用于在Microsoft IIS Web服务器上部署时,无需设置config allowPathInfo选项(IIS> = 7)或元数据库allowPathInfoForScriptMappings(IIS <7)。

默认情况下,IIS提供一个 PATH_INFO,它重复前面的 SCRIPT_NAME,导致希望实现路由的WSGI应用程序的问题。此处理程序将删除任何此类重复的路径。

IIS可以配置为传递正确的 PATH_INFO,但这会导致 PATH_TRANSLATED 错误的另一个错误。幸运的是,这个变量很少使用,不能由WSGI保证。在IIS <7,虽然,设置只能在vhost级别,影响所有其他脚本映射,其中许多当暴露在 PATH_TRANSLATED 错误时中断。为此,IIS <7几乎从未部署与修复。 (即使IIS7很少使用它,因为它仍然没有UI。)

CGI代码无法告诉是否设置了该选项,因此提供了一个单独的处理程序类。它以与 CGIHandler 相同的方式使用,即通过调用 IISCGIHandler().run(app),其中 app 是您要调用的WSGI应用程序对象。

3.2 新版功能.

class wsgiref.handlers.BaseCGIHandler(stdin, stdout, stderr, environ, multithread=True, multiprocess=False)

CGIHandler 类似,但是不是使用 sysos 模块,而是明确地指定CGI环境和I/O流。 multithreadmultiprocess 值用于为处理程序实例运行的任何应用程序设置 wsgi.multithreadwsgi.multiprocess 标志。

此类是 SimpleHandler 的子类,旨在与除“HTTP服务器”之外的软件一起使用。如果您正在编写一个使用 Status: 头来发送HTTP状态的网关协议实现(例如CGI,FastCGI,SCGI等),您可能希望将其子类化,而不是 SimpleHandler

class wsgiref.handlers.SimpleHandler(stdin, stdout, stderr, environ, multithread=True, multiprocess=False)

类似于 BaseCGIHandler,但设计用于HTTP源服务器。如果你正在编写一个HTTP服务器实现,你可能想要子类化这个而不是 BaseCGIHandler

这个类是 BaseHandler 的子类。它覆盖 __init__()get_stdin()get_stderr()add_cgi_vars()_write()_flush() 方法,以支持通过构造函数显式设置环境和流。提供的环境和流存储在 stdinstdoutstderrenviron 属性中。

stdoutwrite() 方法应该完全写入每个块,如 io.BufferedIOBase

class wsgiref.handlers.BaseHandler

这是运行WSGI应用程序的抽象基类。每个实例将处理单个HTTP请求,但原则上您可以创建一个可重用于多个请求的子类。

BaseHandler 实例只有一个用于外部使用的方法:

run(app)

运行指定的WSGI应用程序 app

所有其他 BaseHandler 方法在运行应用程序的过程中由此方法调用,因此主要存在以允许定制该过程。

以下方法必须在子类中重写:

_write(data)

缓冲字节 data 以传输到客户端。没关系,如果这个方法实际上传输数据;当底层系统实际上有这样的区别时,BaseHandler 只是分离写和刷新操作以提高效率。

_flush()

强制缓冲数据传输到客户端。没关系,如果这个方法是一个无操作(即,如果 _write() 实际上发送数据)。

get_stdin()

返回适合用作当前正在处理的请求的 wsgi.input 的输入流对象。

get_stderr()

返回适合用作当前正在处理的请求的 wsgi.errors 的输出流对象。

add_cgi_vars()

将当前请求的CGI变量插入到 environ 属性中。

这里有一些其他的方法和属性,你可能希望覆盖。但是,此列表仅为摘要,不包括可以覆盖的每个方法。在尝试创建自定义 BaseHandler 子类之前,您应该查阅docstrings和源代码以获取更多信息。

自定义WSGI环境的属性和方法:

wsgi_multithread

要用于 wsgi.multithread 环境变量的值。它在 BaseHandler 中默认为true,但在其他子类中可能有不同的默认值(或由构造函数设置)。

wsgi_multiprocess

要用于 wsgi.multiprocess 环境变量的值。它在 BaseHandler 中默认为true,但在其他子类中可能有不同的默认值(或由构造函数设置)。

wsgi_run_once

要用于 wsgi.run_once 环境变量的值。它在 BaseHandler 中默认为false,但默认情况下 CGIHandler 将其设置为true。

os_environ

要包含在每个请求的WSGI环境中的默认环境变量。默认情况下,这是在 wsgiref.handlers 导入时的 os.environ 的副本,但子类可以在类或实例级别创建自己的副本。请注意,字典应该被视为只读,因为默认值在多个类和实例之间共享。

server_software

如果设置了 origin_server 属性,则此属性的值用于设置默认的 SERVER_SOFTWARE WSGI环境变量,并且还要在HTTP响应中设置默认的 Server: 头。对于不是HTTP源服务器的处理程序(例如 BaseCGIHandlerCGIHandler),将被忽略。

在 3.3 版更改: 术语“Python”被替换为实现特定术语,如“CPython”,“Jython”等。

get_scheme()

返回用于当前请求的URL方案。默认实现使用 wsgiref.utilguess_scheme() 函数根据当前请求的 environ 变量猜测方案是“http”还是“https”。

setup_environ()

environ 属性设置为完全填充的WSGI环境。默认实现使用所有上述方法和属性,以及 get_stdin()get_stderr()add_cgi_vars() 方法以及 wsgi_file_wrapper 属性。如果不存在,它还插入 SERVER_SOFTWARE 密钥,只要 origin_server 属性是真值并且 server_software 属性被设置。

定制异常处理的方法和属性:

log_exception(exc_info)

在服务器日志中记录 exc_info 元组。 exc_info(type, value, traceback) 元组。默认实现只是将追溯写入请求的 wsgi.errors 流并将其刷新。子类可以覆盖此方法以更改格式或重定向输出,将追溯邮件发送给管理员,或者任何其他可能被认为合适的操作。

traceback_limit

默认 log_exception() 方法输出的回溯中要包括的最大帧数。如果 None,包括所有帧。

error_output(environ, start_response)

此方法是一个WSGI应用程序为用户生成错误页面。它只有在头发被发送到客户端之前发生错误时才被调用。

此方法可以使用 sys.exc_info() 访问当前错误信息,并且应该在调用它时将该信息传递给 start_response (如 PEP 3333 的“错误处理”部分所述)。

默认实现只是使用 error_statuserror_headerserror_body 属性来生成输出页面。子类可以覆盖此,以产生更多的动态错误输出。

但是,请注意,从安全角度来看,不建议将诊断信息泄露给任何旧用户;理想情况下,您应该做一些特殊的事情来启用诊断输出,这就是为什么默认实现不包括任何。

error_status

用于错误响应的HTTP状态。这应该是 PEP 3333 中定义的状态字符串;它默认为500代码和消息。

error_headers

用于错误响应的HTTP标头。这应该是一个WSGI响应头((name, value) 元组)的列表,如 PEP 3333 中所述。默认列表只是将内容类型设置为 text/plain

error_body

错误响应体。这应该是一个HTTP响应主体bytestring。它默认为纯文本“发生服务器错误,请与管理员联系。

PEP 3333 的“可选的平台特定文件处理”功能的方法和属性:

wsgi_file_wrapper

wsgi.file_wrapper 工厂,或 None。此属性的默认值是 wsgiref.util.FileWrapper 类。

sendfile()

覆盖以实现平台特定的文件传输。仅当应用程序的返回值是由 wsgi_file_wrapper 属性指定的类的实例时,才调用此方法。如果它能够成功传输文件,它应该返回一个真值,以便不执行默认传输代码。此方法的默认实现仅返回false值。

其他方法和属性:

origin_server

如果处理程序的 _write()_flush() 正被用于直接与客户端通信,而不是通过类似于CGI的网关协议(需要特殊 Status: 头中的HTTP状态),则此属性应设置为true值。

此属性的默认值在 BaseHandler 中为true,但在 BaseCGIHandlerCGIHandler 中为false。

http_version

如果 origin_server 为true,则此字符串属性用于将响应集的HTTP版本设置为客户端。它默认为 "1.0"

wsgiref.handlers.read_environ()

Transcode CGI变量从 os.environ 到PEP 3333“unicode”字符串中的字节,返回一个新的字典。这个函数被 CGIHandlerIISCGIHandler 用来代替直接使用 os.environ,这在使用Python 3的所有平台和Web服务器上不一定是WSGI兼容的 - 特别是操作系统的实际环境是Unicode(即Windows)其中环境是字节,但Python使用的解码它的系统编码是除ISO-8859-1之外的任何东西(例如使用UTF-8的Unix系统)。

如果你正在实现一个自己的基于CGI的处理程序,你可能想使用这个例程,而不是直接从 os.environ 复制值。

3.2 新版功能.

21.4.6. 例子

这是一个工作的“Hello World”WSGI应用程序:

from wsgiref.simple_server import make_server

# Every WSGI application must have an application object - a callable
# object that accepts two arguments. For that purpose, we're going to
# use a function (note that you're not limited to a function, you can
# use a class for example). The first argument passed to the function
# is a dictionary containing CGI-style environment variables and the
# second variable is the callable object (see PEP 333).
def hello_world_app(environ, start_response):
    status = '200 OK'  # HTTP Status
    headers = [('Content-type', 'text/plain; charset=utf-8')]  # HTTP Headers
    start_response(status, headers)

    # The returned object is going to be printed
    return [b"Hello World"]

with make_server('', 8000, hello_world_app) as httpd:
    print("Serving on port 8000...")

    # Serve until process is killed
    httpd.serve_forever()