浅谈WSGI
程序员文章站
2022-03-24 11:03:18
...
WSGI :Web Server Gateway Interface ,即 Web 服务器网关接口
WSGI 规定了服务器怎么把请求信息转发给应用,应用怎么把执行情况回传给服务器,服务器与应用都按一个标准办事,只要实现了这个标准,服务器与应用可以实现灵活搭配。
如上图所示:
应用必须是一个可调用对象,可以是函数,也可以是实现了 __call__()
方法的对象。
每收到一个请求,服务器会通过 application_callable(environ, start_response) 调用应用。应用在处理完毕准备返回数据的时候,先调用服务传给它的函数 start_response(status, headers, exec_info),最后再返回可迭代对象作为数据。
其中,environ 必须是一个字典,包括了请求的相关信息,比如请求方式、请求路径等等,start_response 是应用处理完毕后,需要调用的函数,用于告诉服务设置响应的头部信息或错误处理等等。
status 必须是 999 Message here
这样的字符串,比如 200 OK
、404 Not Found
等,headers 是一个由 (header_name, header_value) 这样的元祖组成的列表,最后一个 exec_info 是可选参数,一般在应用出现错误的时候会用到。
知道了 WSGI 的大致概念,简单实现一个。基于python3.7
server.py
# coding=utf-8
import socket
import sys
class WSGIServer:
def __init__(self):
self.listener = socket.socket()
self.listener.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)
self.listener.bind(('0.0.0.0', 8080))
self.listener.listen(1)
print('Serving HTTP on 0.0.0.0'
' port 8080 ...')
self.app = None
self.headers_set = None
def set_app(self, application):
self.app = application
def start_response(self, status, headers):
self.headers_set = [status, headers]
def serve_forever(self):
while True:
listener = self.listener
client_connection, client_address = \
listener.accept()
print(f'Server received connection'
f' from {client_address}')
request = client_connection.recv(1024)
print(f'request we received: {request}')
method, path, _ = request.split(b' ', 2)
# 如果有需要,可以自行补充
environ = {
'wsgi.version': (1, 0),
'wsgi.url_scheme': 'http',
'wsgi.input': request,
'wsgi.errors': sys.stderr,
'wsgi.multithread': False,
'wsgi.multiprocess': False,
'wsgi.run_once': False,
'REQUEST_METHOD': method.decode('utf-8'),
'PATH_INFO': path.decode('utf-8'),
'SERVER_NAME': '127.0.0.1',
'SERVER_PORT': '8080',
}
app_result = self.app(environ, self.start_response)
response_status, response_headers = self.headers_set
response = f'HTTP/1.1 {response_status}\r\n'
for header in response_headers:
response += f'{header[0]}: {header[1]}\r\n'
response += '\r\n'
response = response.encode('utf-8')
for data in app_result:
response += data
client_connection.sendall(response)
client_connection.close()
if __name__ == '__main__':
if len(sys.argv) < 2:
sys.exit('Argv Error')
app_path = sys.argv[1]
module, app = app_path.split(':')
module = __import__(module)
app = getattr(module, app)
server = WSGIServer()
server.set_app(app)
server.serve_forever()
app.py
# coding=utf-8
def simple_app(environ, start_response):
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return [f'Request {environ["REQUEST_METHOD"]}'
f' {environ["PATH_INFO"]} has been'
f' processed\r\n'.encode('utf-8')]
命令行运行:
python server.py app:simple_app
目前流行的 Web 应用框架比如 Django、Bottle 等,服务器 Apahce、Nginx 等也都支持WSGI规范。