简述
WSGI(Web Server Gateway Interface,Web服务器网关接口)是一种描述web server
如何与web application
通信的规范,规定了服务器端和应用程序间的接口。
WSGI不是服务器软件也不是后端框架,只是一种交互协议,实现了这种协议的server和application之间就可以按照协议规定的方式进行交互。再常见的web服务中,server主要负责收集来自客户端的请求数据,按照wsgi接口规范对数据进行封装,并将这个封装的数据交给应用程序(例如django)进行处理,后端应用处理后将结果按照wsgi规定的格式返回给服务器,再由服务器返回数据给客户端。详细的wsgi规范信息在python的PEP333中有具体描述
WSGI协议
WSGI用于application和server之间的交互,详细的规定两种服务之间的交互方式。
- application:它应该实现为一个可调用对象,例如python函数、类,类实例(实现了__
call__
方法),并且需要接收两个参数:- environ:一个字典对象,该字典可以包含了客户端请求的信息以及当前系统中的一些变量信息,可以认为是该次请求的上下文信息,该参数名一般叫做environ(编码中多简写为environ、env)
- start_response:这是一个函数对象,用于发送HTTP响应状态(HTTP status )、响应头(HTTP headers)的信息。可以看作是给application的一个回调函数,方便app进行操作。
- server:可以是实现了wsgi的任意web服务器,再这个服务器中,会调用app这个可调用对象,并向其注入上述的两个参数,常见的uwsgi服务器。
简单实现
Python提供了一个实现了wsgi协议的服务器(wsgiref.simple_server)和应用程序(wsgiref.simple_server.demo_app)。他们满足wsgi接口,可以实现调用。
from wsgiref.simple_server import make_server, demo_app # 导入服务器和demo_app with make_server('127.0.0.1', 8000, demo_app) as httpd: httpd.serve_forever() # app作为参数绑定到服务器中,并启动服务
执行上面的代码,使用浏览器访问本地8000端口,将能够访问该web服务并显示内容。server接受了客户端的请求,并将客户端的请求转交demo_app处理,随后demo_app将结果返回服务器server,服务器将消息发送到客户端,这是请求和回复基本流程。
demoapp的实现原理
浏览器中显示的信息就是来自demoapp的处理。查看源码:
1 # 服务器注入两个参数:environ数据,start_response函数 2 def demo_app(environ,start_response): 3 from io import StringIO 4 stdout = StringIO() 5 print("Hello world!", file=stdout) 6 print(file=stdout) 7 h = sorted(environ.items()) 8 for k,v in h: 9 print(k,'=',repr(v), file=stdout) 10 # 调用函数,将状态码和头部信息消息发送给客户端。 11 start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')]) 12 return [stdout.getvalue().encode("utf-8")] # 返回值必须可迭代对象,内部每一个元素必须是bytes类型
上面我们调用了start_resposne函数。他是一个三参函数,用来返回头信息,至少应该包含上面的参数:start_response(status, response_headers, exc_info=None)
参数名称 | 说明 |
state | 状态码和状态描述 |
response_headers | 一个元素为二元组的列表,包含对象 |
exc info | 在错误处理的时候使用,包含错误信息,使用该参数将错误返回。 |
常用的可定义response_header或者request_header有如下示例
CONTENT_LENGTH = '' CONTENT_TYPE = 'text/plain' GATEWAY_INTERFACE = 'CGI/1.1' HTTP_ACCEPT = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3' HTTP_ACCEPT_ENCODING = 'gzip, deflate, br' HTTP_ACCEPT_LANGUAGE = 'zh-CN,zh;q=0.9,en;q=0.8' HTTP_CONNECTION = 'keep-alive' HTTP_UPGRADE_INSECURE_REQUESTS = '1' HTTP_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36' REMOTE_ADDR = '127.0.0.1' REMOTE_HOST = '' REQUEST_METHOD = 'GET'
实现自己app
了解服务调用app的过程,以及app处理数据后返回的过程,我们可以自己定义一个信息app,而这个app,就是如同djagno和flask那样被调用
函数实现
1 rom wsgiref import simple_server 2 3 def app(environ, start_response): 4 # 状态码 + 后面返回的头信息,后面为二元组们,将来构成k-v对 5 start_response('200 OK', [('Content-Type', 'text/plain; charset=utf-8')]) 6 return ['abc'] 7 8 server = simple_server.make_server('127.0.0.1', 8000, app) 9 server.serve_forever()
类实现
1 class App: # 类的init方法接受参数, 2 def __init__(self, environ, start_response): 3 self.environ = envrion 4 self.start_res = start_response 5 6 def __iter__(self): # 返回值为实例,非可迭代对象,定义iter方法 7 print(self.envrion) 8 self.start_response("200 OK", [('Content-Type', 'text/plain charset=utf-8')]) 9 yield from [b'abc'] 10 11 server = simple_server.make_server('127.0.0.1', 8000, App) 12 server.serve_forever()
类示例实现
1 class Application: 2 def __call__(self, environ, start_response): # 实例可调用,接受参数,返回可迭代对象 3 print(envrion) 4 start_response('200 OK', [('Content-Type',"text/plain charset=utf-8")]) 5 return [b"123"] 6 7 server = simple_server.make_server('127.0.0.1', 8000, Application()) 8 server.serve_forever()
使用函数,类以及类的实例均可以处理来自服务器的请求,并返回结果,通常我们的应用比较复杂,通常使用类进行处理,可扩展性更高,可以执行更加复杂的调用过程。