对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。
对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。
WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。
python标准库提供的独立WSGI服务器称为wsgiref。
WSGI规范
如同 HTTP 协议有一个客户端和一个服务端,WSGI 协议有一个 Application 端和一个 Server 端,其中 Application 就是指 Flask、Django 这些 Web 框架,而 Server 就是指 Gunicorn、uWSGI 等 Web 服务器。
WSGI规定应用程序必须是一个可调用对象(可调用对象可以是函数,也可以是类,还可以是实现了 __call__的实例对象),而且必须接受两个参数,该对象的返回值必须是可迭代对象.
application 是一个函数,可以是可调用对象,然后接收两个参数,两个参数分别是:environ和start_response
-
environ是一个字典,里面储存了HTTP request相关的所有内容,比如header、请求参数等等
-
start_response是一个WSGI 服务器传递过来的函数,用于将response header,状态码传递给Server。
调用 start_response 函数负责将响应头、状态码传递给服务器, 响应体则由application函数返回给服务器, 一个完整的http response 就由这两个函数提供
其接口如下:
def application(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return ['Hello world!\n']
application 就是一个最简单的 应用程序,它需要接收两个参数,environ 是一个 dict,其中保存了所有 HTTP 请求相关的信息,由 Server 端提供,start_response 是一个可调用对象,同样由 Server 端提供,application 内部需要调用一次 start_response,并将 状态码 和 响应头 当作参数传递给它,application 最终会返回一个可迭代对象作为 HTTP Body 内容返回给客户端。
我们已经知道了 应用程序端接口,接下来看下一个符合 WSGI 协议的 Server 端实现:
import osdef wsgi_server(application): environ = dict(os.environ.items()) def start_response(status, response_headers): print(f'status: {status}') print(f'response_headers: {response_headers}') result = application(environ, start_response) for data in result: print(f'response_body: {data}')
示例中 Server 端同样使用函数来实现,wsgi_server 接收一个 application 作为参数,在其内部构造了 environ 和 start_response 两个对象,这里使用环境变量信息来模拟 HTTP 请求信息构造 environ 字典,start_response 同样被定义为一个函数,供 application 在内部对其进行调用,wsgi_server 函数最后会调用 application 并对其进行打印。
现在有了 Application 端和 Server 端,我们可以来测试一下这个简单的 WSGI 程序示例。只需要将 simple_app 作为参数传递给 wsgi_server 并调用 wsgi_server 即可:
wsgi_server(simple_app)
执行以上代码,将得到如下打印:
status: 200 OKresponse_headers: [('Content-type', 'text/plain')]response_body: Hello world!
以上,我们分别实现了符合 WSGI 规范的 Application 端和 Server 端,虽然程序看起来比较简陋,但不论多么复杂的 Python Web 框架和 Server 都同样遵循此规范。