由来

   在早期的HTTP编程中,许多Python服务都是被编写为简单的CGI脚本。每次收到请求就会出发CGI脚本。服务器对HTTP请求进行分割,将相应的参数以环境变量的形式传入CGI脚本中。Python程序员可以直接将这些输入参数和HTTP响应打印到标准输出,也可以借助标准库的cgi模块来查看。
  在上面的方式会为每个接收到HTTP请求启动一个新的进程。这大大影响了服务器的性能,因此各种语言开始着手实现自己的HTTP服务器。Python在标准库中加入了http.server模块。使用该模块来实现服务时,程序员需要编写自己的子类继承BaseHTTPRequestHandler,并添加do_GET()do_POST()方法。
  其一些程序员想在支持返回动态页面的Web服务器上同事支持返回静态内容,因此出现了mod_pythonmod_python是一个Apache模块,允许成功注册的Python函数提供自定义的Apache处理函数,并且在自定义处理函数中提供认证和日志功能以及返回内容。mod_pythonAPIApache独有的。使用Python编写的处理函数接受一个特殊的Apache request对象。在处理函数内部,可以调用apache模块的特殊函数来与Web服务器进行交互。使用mod_python的应用程序与使用CGIhttp.server编写的程序几乎毫无相似之处。
  因此,在Python中使用上述不同方式编写HTTP应用程序,在设计与Web服务器交互的接口时都采用了某种特定的机制。使用CGI方式编写的服务至少需要重写一部分代码才能应用http.server。无论是使用CGI还是http.server编写的服务器程序,都需要经过修改才能够在Apache下运行。这使得Python网络服务的可移植性很差。
  为了解决这一问题,Python社区在PEP 333中提出了Web服务器网管接口(WSGI, Web Server Gateway Interface)。而WSGI标准就是添加了一层中间层。通过这一中间层,用Python编写的HTTP服务就能够与任何Web服务器进行交互了。WSGI标准指定了一个调用惯例,如果所有主流的Web服务器的实现都遵循这一惯例,那么就能够直接在服务器中应用底层服务以及功能完整的Web框架,而无需修改原来的代码。各大Web服务器很快就遵循WSGI进行了实现。现在,WSGI已经成为了使用Python进行HTTP操作的标准方法。

一个简单的HTTP服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python3
# A simple HTTP service built directly against the low-level WSGU spec.

from pprint import pformat
from simple.simple_server import make_server

def app(environ, start_response):
   headers = {"Content-Type": "text/plain; charset=utf-8"}
   start_response("200 OK", list(headers.items()))
   yield "Here is the WSGI environment:\r\n\r\n".encode("utf-8")
   yield pformat(environ).encode("utf-8")

   
if __name__ == "__main__":
   httpd = make_server("", 8000, app)
   host, port = httpd.socket.getsockname()
   print("Serving on", host, "port", port)
   httpd.server_forever()

  根据标准的定义,WSGI应用程序是可以被调用的,并且有两个输入参数。上面代码则是一个展示例子,其中使用一个简单的Python函数来表示可调用的WSGI应用程序。第一个参数是environ,用于接受一个字典,字典中提供的键值对是旧式的CGI环境变量集合的扩展。第二个参数本身是可以被调用的,习惯上会将其命名为start_response()WSGI应用程序通过start_response()来声明响应头信息。被调用后,app函数可以开始生成字节字符串,也可以返回一个可迭代对象。该对象可以在迭代过程中生成字符串。
  只看代码的话,你可能会觉得WSGI很简单,这是因为上面的代码只选择了一种简单的处理方式,没有展示出WSGI标准的完整说明而已。实际编写服务器程序时,复杂度就大大提升了。如果想了解相关细节的话,可以阅读PEP 3333提出的现代Python3版本的WSGI

发展

  WSGI出现后,WSGI中间件的思想开始广为流传。未来Python服务可能应该被设计为一系列WSGI包装的函数。其中,有的包装函数会提供认证功能;有的会负责捕捉异常,记录日志;而有的则负责对指向仍然在机构中运行的老旧的CMSURL进行反向代理,使用Diazo将这些URL重构,使之指向项目中最新页面。
  尽管仍然有一些开发者会编写并使用WSGI中间件,但是现在大多数Python程序员使用WSGI的主要原因是它提供应用程序或框架与监听HTTP请求的Web服务器之间的可插拔性。