WSGI
什么是WSGI
WSGI
:全称是Web Server Gateway Interface,WSGI协议是描述web server如何与web application通信的规范。server和application的规范在PEP 3333中有具体描述。要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议之上的web框架有Torando,Flask,Django等。
WSGI协议主要包括server、application和middlewares部分
WSGI协议其实是定义了一种server与application解耦的规范,即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,可以选择任意的server和application组合实现自己的web应用。
例如:uWSGI和Gunicorn都是实现了WSGI server协议的服务器;Django,Flask是实现了WSGI application协议的web框架,可以根据项目实际情况搭配使用。
WSGI server
WSGI server
负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端;
WSGI application
WSGI application
接收由server转发的request,处理请求,并将处理结果返回给server。
中间件
application中可以包括多个
中间件(middlewares)
,这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用:对服务器来说,中间件扮演应用程序,对应用程序来说,中间件扮演服务器。
实现简单的WSGI
实现WSGI协议必须要有wsgi server和application,一般application需要我们自己实现。
有很多符合WSGI规范的服务器,这里使用python内置的wsgiref模块,它是用纯Python编写的WSGI服务器的参考实现。
application是一个函数
from wsgiref.simple_server import make_server
# 创建app,接收两个参数environ,start_response
# 第一个参数environ就是一个字典,里面存着环境变量,里面的内容和CGI的环境变量基本一致。
# 第二个参数start_response是一个函数,用于输出HTTP的响应头,start_response一般有两个参数:第一个参数是就是状态码,比如200 OK,302 Found之类的。第二个参数则是HTTP首部的其他信息,是一个多个元组构成的list。每个首部字段,都放置到一个元组之中。
# 最终的返回值,就是HTTP响应的实体部分了,必须是一个可迭代对象(python3中需要编码为bytes类型)。
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return ['<h1>Hello, web!</h1>'.encode("utf-8")]
# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('127.0.0.1', 8890, application)
# 开始监听HTTP请求:
httpd.serve_forever()
application是一个对象
from wsgiref.simple_server import make_server
# 可调用对象是一个 Application对象
class Application(object):
def __init__(self):
pass
def __call__(self, environ, start_response):
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return ["Hello world!\n".encode("utf-8")]
# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
app = Application()
httpd = make_server('127.0.0.1', 8890, app)
# 开始监听HTTP请求:
httpd.serve_forever()
实现简单的路由
import re
from wsgiref.simple_server import make_server
def application(environ, start_response):
method = environ["REQUEST_METHOD"].lower()
path = environ["PATH_INFO"].lstrip("/")
if path == "favicon.ico":
start_response('200 OK', [('Content-Type', 'text/html')])
return ["".encode("utf-8")]
res = None
for url, controller in urls:
if re.match(url, path):
if isinstance(controller, type) and hasattr(controller, method):
res = getattr(controller(), method)()
else:
start_response('502 Bad Gateway', [('Content-Type', 'text/html')])
return ["Invalid method".encode("utf-8")]
start_response('200 OK', [('Content-Type', 'text/html')])
return [res.encode("utf-8")]
class HelloController(object):
def get(self, *args, **kwargs):
return "Hello world"
def post(self, *args, **kwargs):
pass
urls = [
(r"hello", HelloController)
]
# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('127.0.0.1', 8890, application)
# 开始监听HTTP请求:
httpd.serve_forever()
支持HTTPS的web server
wsgiref不支持https,可以借助第三方模块gevent和eventlet都可以完成:
from eventlet import wsgi
import eventlet
wsgi.server(eventlet.wrap_ssl(eventlet.listen(('', 8443)),
certfile='cert.crt',
keyfile='private.key',
server_side=True),
application)
其中certfile和keyfile是服务端的证书和私钥文件,可用openssl命令生成自签名的证书和私钥。
虽然使用了一个第三方库,启动server的代码与wsgiref模块不太相同,但是之前编写的application回调函数可以直接在这里使用。也就是说我们的回调是与具体server无关的,更换另一个支持wsgi协议的server,只是在启动的代码做些修改,而我们所关心的业务逻辑则丝毫无需改动。
https://zhuanlan.zhihu.com/p/25939345