本文介绍了 WSGI 是什么以及 WSGI 的处理流程。
服务器网关接口(WSGI)是 web 服务器和 web 应用(或 Python 框架)之间的接口规范,旨在提高 web 应用在 web 服务器上的可迁移性。如果一个应用遵循了 WSGI 规范,那么这个应用可以运行在任何遵循 WSGI 规范的服务器上。
服务器指任何实现了 WSGI 规范的服务器,比如 Gunicorn、uWSGI。
WSGI 有两端:
- 服务器或者说网关端
- 应用端或者说框架端
服务器调用一个由应用端提供的可调用对象。由服务器定义客户端应该如何提供这个对象。比如一些服务器需要应用的部署者去写一个简短的脚本来创建一个服务器的实例,并向服务器实例提供应用对象。另一些服务器可能使用配置文件或者其他机制来指定应用对象应该从哪里导入。
除了单纯的服务器和应用端,也可以创建一个中间件来实现两边的定义。这样的组件对于服务器来说是应用端,而对于应用端来说是服务器,并且可以被用来提供扩展 APIs、内容转换、导航和其他功能。
可调用对象可以指一个函数、方法、类或者一个拥有 __call__
方法的实例。由服务器或实现其的应用端来选择针对他们需求的合适技术。相反的,一个执行可调用对象的服务器或者客户端对提供给它们的可调用对象不能有任何假设。可调用对象只是被用来调用,不需要检查它。
流程
- 客户端发起请求到 WSGI 服务器
- 服务器调用参数为
以请求数据组成的字典
和处理函数
的应用可调用对象 - 应用可调用对象通过处理函数设置 HTTP status、返回头
- 应用可调用对象返回响应体
- 服务器发送响应的 HTTP 状态、头部、响应体给客户端
如果有中间件,那么中间件位于服务器和应用之间,中间件相对于服务器是应用,相对于应用是服务器。
字符串类型
WSGI 定义了两种字符串:
- 原生字符串,用于请求/响应头和元数据(Python 3 中的
str
) - 二进制字符串(在Python 3 中使用
bytes
实现,Python 2 使用str
),用于请求和响应的 body(比如 POST/PUT 的输入数据和 HTML 输出)
应用端
- 应用对象是一个接收两个参数的可调用对象:
- 一个包含请求数据的字典
- 一个用来设置响应 HTTP 状态和头部的函数
- 向服务器返回包含在可迭代对象中的字符串(比如
['str1', 'str2', ]
)作为响应体
应用对象必须可以被执行多次,实际上所有的服务器(除了 CGI) 都会发送重复的请求。
服务器
每当服务器收到一个 HTTP 客户端的请求,服务器会执行应用的可调用对象。获取结果后,将其发送给客户端。
代码
定义可调用应用对象,并启动 WSGI 服务器:
#! /usr/bin/env python
# Python's bundled WSGI server
from wsgiref.simple_server import make_server
def application(environ, start_response):
# Sorting and stringifying the environment key, value pairs
response_body = [
'%s: %s' % (key, value) for key, value in sorted(environ.items())
]
response_body = '
'.join(response_body)
# Response body must be bytes
response_body = response_body.encode()
status = '200 OK'
response_headers = [
('Content-Type', 'text/plain'),
('Content-Length', str(len(response_body)))
]
start_response(status, response_headers)
return [response_body]
# Instantiate the server
httpd = make_server(
'localhost', # The host name
8051, # A port number where to wait for the request
application # The application object name, in this case a function
)
# Wait for a single request, serve it and quit
httpd.handle_request()
以上代码来自 http://wsgi.tutorial.codepoint.net/environment-dictionary
启动 WSGI 服务后,访问 http://localhost:8051/ 可获得响应。
服务器调用应用得到响应后,将其发送给客户端
# 来自 wsgiref.handlers.BaseHandler#finish_response
def finish_response(self):
try:
if not self.result_is_file() or not self.sendfile():
for data in self.result:
self.write(data)
self.finish_content()
finally:
self.close()