wsgiref ——wsgi实用程序和参考实现


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

只有Web服务器和编程框架的作者才需要了解WSGi设计的每个细节和关键案例。您不需要仅仅为了安装一个WSGi应用程序或使用现有框架编写一个Web应用程序而理解WSGi的每一个细节。

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

wsgi.readthedocs.io 有关wsgi的更多信息,以及到教程和其他资源的链接。

wsgiref.util --WSGi环境实用程序

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

wsgiref.util.guess_scheme(environ)

对是否 wsgi.url_scheme 应该是“http”或“https”,通过检查 HTTPS 中的环境变量 环境 字典。返回值是一个字符串。

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

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

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

wsgiref.util.application_uri(environ)

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

wsgiref.util.shift_path_info(environ)

将单个名称从 PATH_INFOSCRIPT_NAME 并返回名称。这个 环境 字典是 被改进的 到位;如果需要保留原件,请使用副本 PATH_INFOSCRIPT_NAME 完整的。

如果中没有剩余的路径段 PATH_INFONone 返回。

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

什么时候? PATH_INFO 只是一个“/”,此例程返回一个空字符串,并将尾随斜杠追加到 SCRIPT_NAME ,即使通常忽略空路径段,并且 SCRIPT_NAME 通常不会以斜线结尾。这是有意的行为,以确保应用程序能够区分以 /x 从最后一个 /x/ 使用此例程进行对象遍历时。

wsgiref.util.setup_testing_defaults(environ)

更新 环境 为了测试的目的,使用一些简单的默认值。

这个例程添加了wsgi所需的各种参数,包括 HTTP_HOSTSERVER_NAMESERVER_PORTREQUEST_METHODSCRIPT_NAMEPATH_INFO 以及所有 PEP 3333 定义的 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)

返回 True 如果“header_name”是HTTP/1.1“Hop-by-Hop”头,则由 RFC 2616 .

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

将类似文件的对象转换为 iterator . 结果对象同时支持 __getitem__()__iter__() 迭代样式,与Python2.1和Jython兼容。当对象被迭代时,可选的 chunksize 参数将重复传递到 类丝状的 对象的 read() 方法获取要生成的字节数。什么时候? read() 返回空的bytestring,迭代结束,不可恢复。

如果 类丝状的 有一个 close() 方法,返回的对象还将具有 close() 方法,它将调用 类丝状的 对象的 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)

3.8 版后已移除: 支持 sequence protocol 被贬低。

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() 如果存在多值头,则可以多次包含同一个键。这个 len() A的 Headers 对象的长度与其 items() ,与换行标题列表的长度相同。事实上, items() 方法只返回封装头列表的副本。

调用 bytes() 在一 Headers 对象返回适合作为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 参数是可选的。

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

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

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

创建新的WSGi服务器侦听 hostport ,接受的连接 app . 返回值是所提供的 server_class ,并将使用指定的 handler_class . app 必须是wsgi应用程序对象,定义如下 PEP 3333 .

示例用法:

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!”的文本页。以及在 环境 参数。它对于验证wsgi服务器(例如 wsgiref.simple_server )能够正确运行一个简单的wsgi应用程序。

class wsgiref.simple_server.WSGIServer(server_address, RequestHandlerClass)

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

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

WSGIServer 是的子类 http.server.HTTPServer ,所以它的所有方法(例如 serve_forever()handle_request() )可用。 WSGIServer 还提供了这些特定于WSGi的方法:

set_app(application)

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

get_app()

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

但是,通常不需要使用这些附加方法,因为 set_app() 通常由 make_server()get_app() 主要为了请求处理程序实例的利益而存在。

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

为给定的创建HTTP处理程序 请求 (即Socket) client_address (A) (host,port) 元组) 服务器 (WSGIServer 实例)。

您不需要直接创建这个类的实例;它们是根据需要自动创建的 WSGIServer 物体。但是,您可以将该类划分为子类,并将其作为 handler_classmake_server() 功能。在子类中重写的一些可能相关的方法:

get_environ()

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

get_stderr()

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

handle()

处理HTTP请求。默认实现使用 wsgiref.handlers 类来实现实际的wsgi应用程序接口。

wsgiref.validate ---WSGI一致性检查器

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

请注意,此实用程序不能保证完成 PEP 3333 合规性;此模块中没有错误并不一定意味着不存在错误。但是,如果这个模块确实产生了错误,那么实际上可以确定服务器或应用程序不是100%兼容的。

此模块基于 paste.lint 来自IanBicking的“python-paste”库的模块。

wsgiref.validate.validator(application)

包裹 应用 并返回一个新的wsgi应用程序对象。返回的应用程序将所有请求转发到原始 应用 并将检查 应用 调用它的服务器符合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()

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

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

class wsgiref.handlers.CGIHandler

基于CGI的调用通过 sys.stdinsys.stdoutsys.stderros.environ . 当您有一个wsgi应用程序并希望以CGI脚本的形式运行它时,这很有用。简单调用 CGIHandler().run(app) 在哪里 app 是要调用的WSGi应用程序对象。

这个类是 BaseCGIHandler 那套 wsgi.run_once 说真的, wsgi.multithread 假的,以及 wsgi.multiprocess 为真,并始终使用 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流。这个 多线程多进程 值用于设置 wsgi.multithreadwsgi.multiprocess 由处理程序实例运行的任何应用程序的标志。

这个类是 SimpleHandler 用于HTTP“源服务器”以外的软件。如果您正在编写使用 Status: 要发送HTTP状态,您可能需要将其子类化,而不是 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 属性。

这个 write() 方法 stdout 应该完整地编写每个块,比如 io.BufferedIOBase .

class wsgiref.handlers.BaseHandler

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

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

run(app)

运行指定的wsgi应用程序, app .

所有其他的 BaseHandler 方法在运行应用程序的过程中被此方法调用,因此主要存在于允许自定义过程的情况下。

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

_write(data)

缓冲字节 data 用于传输给客户。如果这个方法真的传输数据是可以的; BaseHandler 当底层系统实际上有这样的区别时,只需将写操作和刷新操作分开,以提高效率。

_flush()

强制将缓冲数据传输到客户端。如果这个方法是一个no-op(即,如果 _write() 实际发送数据)。

get_stdin()

返回适合用作 wsgi.input 当前正在处理的请求。

get_stderr()

返回适合用作 wsgi.errors 当前正在处理的请求。

add_cgi_vars()

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

下面是一些您可能希望重写的其他方法和属性。然而,这个列表只是一个摘要,不包括所有可以重写的方法。在尝试创建自定义的 BaseHandler 子类。

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

wsgi_multithread

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

wsgi_multiprocess

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

wsgi_run_once

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

os_environ

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

server_software

如果 origin_server 属性已设置,此属性的值用于设置默认值 SERVER_SOFTWARE wsgi环境变量,以及设置默认值 Server: HTTP响应中的头。对于处理程序(例如 BaseCGIHandlerCGIHandler )不是HTTP源服务器。

在 3.3 版更改: 术语“python”替换为特定于实现的术语,如“cpython”、“jython”等。

get_scheme()

返回当前请求使用的URL方案。默认实现使用 guess_scheme() 函数来自 wsgiref.util 根据当前请求的 environ 变量。

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 “S”可选平台特定文件处理“功能:

wsgi_file_wrapper

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

sendfile()

覆盖以实现平台特定的文件传输。仅当应用程序的返回值是由 wsgi_file_wrapper 属性。如果能够成功传输文件,它应该返回一个真值,这样默认的传输代码就不会被执行。此方法的默认实现只返回一个错误值。

其他方法和属性:

origin_server

如果处理程序的 _write()_flush() 正被用来直接与客户机通信,而不是通过一个类似CGI的网关协议,该协议希望在一个特殊的 Status: 标题。

此属性的默认值在中为真 BaseHandler 但在 BaseCGIHandlerCGIHandler .

http_version

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

wsgiref.handlers.read_environ()

将CGI变量从 os.environPEP 3333 “bytes in unicode”字符串,返回新字典。此函数由使用 CGIHandlerIISCGIHandler 代替直接使用 os.environ 在所有使用python 3的平台和Web服务器上,这不一定符合wsgi——特别是操作系统的实际环境是unicode(即windows)的平台和Web服务器,或者环境是字节的平台和Web服务器,但python用于解码的系统编码与iso-8859-1(如使用utf-8的Unix系统)不同。

如果您正在实现自己的基于CGI的处理程序,那么您可能希望使用此例程,而不只是从 os.environ 直接。

3.2 新版功能.

实例

这是一个正常工作的“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.
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()

为当前目录提供服务的wsgi应用程序示例,在命令行上接受可选目录和端口号(默认值:8000):

#!/usr/bin/env python3
'''
Small wsgiref based web server. Takes a path to serve from and an
optional port number (defaults to 8000), then tries to serve files.
Mime types are guessed from the file names, 404 errors are raised
if the file is not found. Used for the make serve target in Doc.
'''
import sys
import os
import mimetypes
from wsgiref import simple_server, util

def app(environ, respond):

    fn = os.path.join(path, environ['PATH_INFO'][1:])
    if '.' not in fn.split(os.path.sep)[-1]:
        fn = os.path.join(fn, 'index.html')
    type = mimetypes.guess_type(fn)[0]

    if os.path.exists(fn):
        respond('200 OK', [('Content-Type', type)])
        return util.FileWrapper(open(fn, "rb"))
    else:
        respond('404 Not Found', [('Content-Type', 'text/plain')])
        return [b'not found']

if __name__ == '__main__':
    path = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
    port = int(sys.argv[2]) if len(sys.argv) > 2 else 8000
    httpd = simple_server.make_server('', port, app)
    print("Serving {} on port {}, control-C to stop".format(path, port))
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("Shutting down.")
        httpd.server_close()