# 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）的网关时，此功能非常有用。通常，提供这样的协议的服务器将包括具有值“1”“是”的“`HTTPS`变量​​或当经由SSL接收请求时”on“。因此，如果找到这样的值，此函数返回“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_INFO`和`QUERY_STRING`变量。结果是由请求寻址的应用程序对象的基本URI。

`wsgiref.util.``shift_path_info`(environ)

  将单个名称从`PATH_INFO`切换到`SCRIPT_NAME`，并返回名称。environ字典已修改到位；如果您需要保留原始的`PATH_INFO`或`SCRIPT_NAME`，请使用副本。

  如果`PATH_INFO`中没有剩余路径段，则返回`None`。

  通常，此例程用于处理请求URI路径的每个部分，例如将路径视为一系列字典键。此例程修改传入的环境以使其适合于调用位于目标URI的另一WSGI应用程序。例如，如果在`/foo`处有一个WSGI应用程序，并且请求URI路径是`/foo/bar/baz`，并且WSGI应用程序位于`/foo`调用`shift_path_info()`，它将接收字符串“bar”，并且环境将被更新为适合于在`/foo/bar` 也就是说，`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以进行测试。

  This routine adds various parameters required for WSGI, including `HTTP_HOST`, `SERVER_NAME`, `SERVER_PORT`, `REQUEST_METHOD`, `SCRIPT_NAME`, `PATH_INFO`, and all of the PEP 3333-defined `wsgi.*` variables. 它仅提供默认值，不会替换这些变量的任何现有设置。

  此例程旨在使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

      httpd = make_server('', 8000, simple_app)
      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参数将被重复传递给文件样 t>对象的`read()`方法，以获取bytestrings to yield。当`read()`返回一个空的字节时，迭代结束，不能恢复。

  If filelike has a `close()` method, the returned object will also have a `close()` method, and it will invoke the filelike object’s `close()` method when called.

  用法示例：

      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` objects support typical mapping operations including `__getitem__()`, `get()`, `__setitem__()`, `setdefault()`, `__delitem__()` and `__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.util`的`shift_path_info()`函数。）

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

  创建在host和port上侦听的新WSGI服务器，接受app的连接。返回值是提供的server_class的实例，将使用指定的handler_class处理请求。app必须是由 PEP 3333定义的WSGI应用程序对象。

  用法示例：

      from wsgiref.simple_server import make_server, demo_app

      httpd = make_server('', 8000, demo_app)
      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()`函数可以处理所有详细信息。

  `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处理程序。套接字），client_address（`(host,port)`元组）和服务器（`WSGIServer`

  您不需要直接创建此类的实例；它们会根据`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)

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

  任何检测到的不符会导致`AssertionError`出现；请注意，这些错误的处理方式取决于服务器。例如，`wsgiref.simple_server`和其他基于`wsgiref.handlers`的服务器（不覆盖错误处理方法来执行其他操作）只会输出一条消息，错误，并将跟踪转储到`sys.stderr`或某个其他错误流。

  此封装程序还可以使用`warnings`模块生成输出，以指示有问题但可能未被 PEP 3333禁止的行为。除非使用Python命令行选项或`warnings` API抑制它们，否则任何此类警告都将写入`sys.stderr`（而不是 `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)

      httpd = make_server('', 8000, validator_app)
      print("Listening on port 8000....")
      httpd.serve_forever()

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

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

class `wsgiref.handlers.``CGIHandler`

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

  This class is a subclass of `BaseCGIHandler` that sets `wsgi.run_once` to true, `wsgi.multithread` to false, and `wsgi.multiprocess` to true, and always uses `sys` and `os` to obtain the necessary CGI streams and environment.

class `wsgiref.handlers.``IISCGIHandler`

  `CGIHandler`的特殊替代，用于在Microsoft的IIS Web服务器上部署时，无需设置config allowPathInfo选项（IIS> = 7）或metabase allowPathInfoForScriptMappings（IIS

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

  IIS可以配置为传递正确的`PATH_INFO`，但这会导致`PATH_TRANSLATED`错误的另一个错误。幸运的是，这个变量很少使用，不能由WSGI保证。On IIS<7, though, the setting can only be made on a vhost level, affecting all other script mappings, many of which break when exposed to the `PATH_TRANSLATED` bug. 出于这个原因IIS（即使IIS7很少使用它，因为它仍然没有UI。）

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

  版本3.2中的新功能。

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

  与`CGIHandler`类似，但是不是使用`sys`和`os`模块，而是明确指定CGI环境和I / O流。多线程和多进程值用于为任何应用程序运行设置`wsgi.multithread`和`wsgi.multiprocess`标志由处理程序实例。

  此类是`SimpleHandler`的子类，旨在与HTTP“源服务器”以外的软件一起使用。如果您正在编写网关协议实现（如CGI，FastCGI，SCGI等）使用`Status:`标头发送HTTP状态，您可能希望将其子类化，而不是`SimpleHandler`。

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

  类似于`BaseCGIHandler`，但设计为与HTTP源服务器一起使用。如果您正在编写HTTP服务器实现，您可能希望将其子类化，而不是`BaseCGIHandler`。

  此类是`BaseHandler`的子类。It overrides the `__init__()`, `get_stdin()`, `get_stderr()`, `add_cgi_vars()`, `_write()`, and `_flush()` methods to support explicitly setting the environment and streams via the constructor. 提供的环境和流存储在`stdin`，`stdout`，`stderr`和`environ`属性中。

  stdout的`write()`方法应该完全写入每个块，如`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环境变量，并设置默认的`Server:` HTTP响应中的标头。对于不是HTTP源服务器的处理程序（例如`BaseCGIHandler`和`CGIHandler`）将被忽略。

    在版本3.3中更改：术语“Python”替换为实现特定术语，如“CPython”，“Jython”等。

  `get_scheme`()

    返回用于当前请求的URL方案。默认实现使用来自`wsgiref.util`的`guess_scheme()`函数，根据当前请求的`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是`（类型， 值， 追逆）`元组。默认实现只是将追溯写入请求的`wsgi.errors`流并清空它。子类可以覆盖此方法以更改格式或重新定位输出，将追溯邮件发送给管理员或任何其他可能被认为合适的操作。

  `traceback_limit`

    默认`log_exception()`方法输出的跟踪回调中要包括的最大帧数。如果`None`，则包括所有帧。

  `error_output`(environ, start_response)

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

    此方法可以使用`sys.exc_info()`访问当前错误信息，并应在调用时将该信息传递到start_response（如“错误处理” PEP 3333）。

    默认实现仅使用`error_status`，`error_headers`和`error_body`属性来生成输出页面。子类可以覆盖此，以产生更多的动态错误输出。

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

  `error_status`

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

  `error_headers`

    用于错误响应的HTTP标头。This should be a list of WSGI response headers (`(name, value)` tuples), as described in PEP 3333. 默认列表只是将内容类型设置为`text/plain`。

  `error_body`

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

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

  `wsgi_file_wrapper`

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

  `sendfile`()

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

  其他方法和属性：

  `origin_server`

    如果处理程序的`_write()`和`_flush()`正在用于直接与客户端通信，而不是通过CGI类似的方式，则此属性应设置为true网关协议，其希望HTTP状态在特殊的`Status:`头中。

    此属性的默认值在`BaseHandler`中为true，但在`BaseCGIHandler`和`CGIHandler`中为false。

  `http_version`

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

`wsgiref.handlers.``read_environ`()

  将`os.environ`中的CGI变量转换为PEP 3333“unicode”字符串中的字节，返回一个新字典。此函数由`CGIHandler`和`IISCGIHandler`使用，而不是直接使用`os.environ`，这在所有平台和Web上不一定符合WSGI服务器使用Python 3 - 特别是，其中操作系统的实际环境是Unicode（即 Windows），或者环境是字节的，但是Python使用的解码它的系统编码是除ISO-8859-1之外的任何编码。使用UTF-8的Unix系统）。

  如果您正在实现自己的基于CGI的处理程序，您可能希望使用此例程，而不是直接复制`os.environ`中的值。

  版本3.2中的新功能。

## 21.4.6. Examples

这是一个工作的“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"]

    httpd = make_server('', 8000, hello_world_app)
    print("Serving on port 8000...")

    # Serve until process is killed
    httpd.serve_forever()