一个web应用的本质
- 浏览器发送一个HTTP请求;
- 服务器收到请求,生成一个HTML文档;
- 服务器把HTML文档作为HTTP响应的Body发送给浏览器;
- 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。
思考:HTML文档需要程序员编写代码动态生成,但解析请求和发送响应的代码是不是可以固定下来???
我们一直在编写业务代码,却从未关心过如何发送响应,谁来监听请求?
为了让我们能专注于编写业务,所以底层的解析请求及发送响应需要有一个专门的接口,这个接口就是WSGI。
- WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server如何与web application通信的规范,实现了server和application的解耦。当前运行在WSGI协议之上的web框架有Bottle, Flask, Django。
- uWSGI:是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等
- uwsgi:是一个uWSGI服务器自有的协议,它用于定义传输信息的类型(type of information),每一个uwsgi packet前4byte为传输信息类型描述
图示
接口定义及测试DEMO展示
首先我们思考一下,我们我们自己去编写一个uWSGI服务器,我们应该怎么让开发web应用的程序员更专注的去编写业务?而不再去操心解析请求和发送响应,我们可能会这么做:要求开发者声明一个函数,服务器负责把请求参数传给这个函数,然后函数返回给服务器一个响应,服务器负责把响应返回给客户端。
WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求。
- demo
def application(environ, start_response):
'''
:param environ: 一个包含所有HTTP请求信息的dict对象;
:param start_response:一个发送HTTP响应的函数,接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header
:return:
'''
start_response('200 OK', [('Content-Type', 'text/html')])
return '<h1>Hello, web!</h1>'
- 开发环境下,我们可以使用Python内置的测试服务器,这个模块叫wsgiref,它是用纯Python编写的WSGI服务器的参考实现,这个模块目前django框架在使用。所谓“参考实现”是指该实现完全符合WSGI标准,但是不考虑任何运行效率,仅供开发和测试使用
#从wsgiref模块导入:
from wsgiref.simple_server import make_server
#创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('', 8000, application)
print("Serving HTTP on port 8000...")
#开始监听HTTP请求:
httpd.serve_forever()
或者使用Flask开发环境下的一个叫werkzeug的库
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 9000, hello)
查看environ参数
这里可以打断点调试给大家看
- 总结
- 通过WSGI定义,我们可以发现,无论多么复杂的框架,只需要提供给uWSGI服务器一个可调用对象,在Python中,可以是一个函数,可以是一个类的实例!
想开发一个web框架,只需要实现一个可调用的对象,比如Flask中我们创建的app对象,无论这个app对象中间经历多长的流程,终归要返回一个response,其他具体的功能我们在框架内自由发挥即可。
流程:创建web应用=>交给uWSGI服务器=>开启服务,完事!
- 通过WSGI定义,我们可以发现,无论多么复杂的框架,只需要提供给uWSGI服务器一个可调用对象,在Python中,可以是一个函数,可以是一个类的实例!
寻找框架运行入口
- Python中什么叫可调用对象?
def application():
return '<h1>Hello, web!</h1>'
print(callable(application))
print(dir(application))
print(application.__call__())
- 如何实现让一个类的实例可调用?
class A:
pass
a = A()
print(callable(a))
class A:
def __call__(self, *args, **kwargs):
return '我也可以 像函数一样被调用'
a = A()
print(callable(a))
print(a())
- 通过前面wsgi的分析,我们知道当你传给给服务器一个可调用对象时,服务器就加上括号调用了,因此现在我们确定了框架的入口,抛开服务器端不管,我们直接去flask框架里找到这个应用的
"__call__"
方法就可以了.