WSGI的全称是Web Server GateWay Interface,Web服务器网关接口,是Python定义的一个服务器与web应用通信的接口。嗯,看了定义还是不懂这到底是什么,没关系,我们接着往下看。
1.web开发遇到的问题
假设我们现在想用Python开发一个 “Hello World” 级别的HTTP Web应用。在不借助任何其他已有的组件情况下,如何开始呢?首先,不管应用有多简单,也要实现一个服务器。服务器要一直监听TCP的80端口,当发现有内容传入时,把内容解析为HTTP数据包。怎么解析呢?从TCP接收到的内容实际是由各种分隔符和字符组成的HTTP报文,必须遵循HTTP协议的规范,才能正确解析,所以还要先研究RFC文档,可能光看文档就得几个月(头秃);最后按照HTTP协议的规范把想返回的内容加上HTTP头,传给TCP,这才是一个完整的服务器处理周期。这仅仅是服务器的部分,接下来才能关注Web应用。
好在我们不必自己从头写Web服务器,已经有别人写好了,我们只需关注Web 的应用层,然后把这个应用交给服务器就行。那么问题又来了,服务器和Web应用如何通信呢?写服务器的是一帮人,写应用层的是另一群人,需要制定统一的接口,这样两边才能正常通信。在这种情况下,诞生了WSGI。WSGI的位置大致如下图所示:
现在,我们要是想实现一个Web应用,只需关注应用层面,遵循WSGI接口即可,而无需关注底层的服务器。
2.一个最简单的WSGI应用
实现一个遵循WSGI的应用很简单:
def application(environ,start_response):
start_response(status,response_headers)
return [response_body]
如上所示,满足的条件有:
- 该应用必须是一个可调用对象,无论是函数、方法还是一个实现了__call__ 方法的对象;
- 应用必须接收两个参数,一般用
environ
和start_response
,这两个参数都是由服务器传递进来;前一个是字典,包含了HTTP请求的一些字段(CGI字段);后一个是回调函数,由此应用调用,给Web服务器返回HTTP状态码和其他头部,状态码应为string,而头部应为由二元tuple组成的 list; - 最后该应用需要返回一个HTTP body,是一个bytes组成的可迭代对象(如list)。
按照这样的接口规范,我们现在来写一个返回 “Hello World” 的Web应用:
def application(environ,start_response):
if environ["REQUEST_METHOD"] == "GET": # 判断请求方法是否是GET
start_response('200 OK',[('Content-Type', 'text/html')]) # 回调
return [b'Hello World'] # 返回一个可迭代对象
代码很简短,先判断请求方法,再回调,最后返回内容。函数的两个参数,environ
和start_response
将会由服务器传递进来。
现在我们只需要一个符合WSGI标准的服务器,把这个函数交给服务器运行即可。Python内置了一个简易的服务器,我们可以通过wsgiref
来使用:
from wsgiref.simple_server import make_server
httpd=make_server('127.0.0.1',port=9005,app=application)
if __name__ == "__main__":
https.serve_forever()
我们把application
这个函数对象传给了服务器,启动后就可以在本地9005端口监听客户端请求了。用curl
命令测试一下:
$ curl http://localhost:9005
Hello World
到这里我们已经对WSGI有了足够的了解。