Werkzeug is an HTTP and WSGI utility library for Python.提供:
1.WSGI server
类似于tomcat
,是一个server 容器
2.对request
和response
的封装
3.路由处理,将url和func联系起来
所有的python web
都要遵循WSGI
协议,每个python web
都是一个callable对象,就像app = Flask(__name__)
创建出来的app
一样.下图中
client
就是我们的浏览器,比如chrome,edge,firefox等
server
就是apache,nginx,tomcat,和我们要讲的wekzug.WSGIServer
application
就是springmvc,springboot,或者flask
WSGI
规定了Server
和Application
通信的规范,比如app(environ,start_response)
接口,Server
会调用Application
,并传递environ
(请求的所有信息),和start_response
(Application处理完需要调用的函数,参数是状态码,响应头等)
1.从flask讲起,这里用的是最初的版本(v0.0.1).
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run()
2.app.run()
是入口,查找run
方法
def run(self, host='localhost', port=5000, **options):
from werkzeug import run_simple
if 'debug' in options:
self.debug = options.pop('debug')
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
return run_simple(host, port, self, **options) # self 就是Flask(__name__)自身
3.run_simple
就是监听指定port
,收到HTTP
时解析为WSGI
格式,然后调用app
来处理逻辑
WSGI
要给app传入environ
和start_response
两个参数,所以要找app
的__call__
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`"""
return self.wsgi_app(environ, start_response)
4.可以看到__call__
返回的是调用self.wsgi_app
,之所以没把self.wsgi_app
内容写在__call__
中,是因为以后添加中间件方便,
例如app.wsgi_app = MyMiddleware(app.wsgi_app)
def wsgi_app(self, environ, start_response):
with self.request_context(environ):
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
response = self.make_response(rv)
response = self.process_response(response)
return response(environ, start_response)
5.首先看self.request_context
def request_context(self, environ):
return _RequestContext(self, environ)
直接调用的是_RequestContext(self, environ)
class _RequestContext(object):
def __init__(self, app, environ):
self.app = app
self.url_adapter = app.url_map.bind_to_environ(environ)
self.request = app.request_class(environ)
self.session = app.open_session(self.request)
self.g = _RequestGlobals()
self.flashes = None
def __enter__(self):
_request_ctx_stack.push(self)
def __exit__(self, exc_type, exc_value, tb):
if tb is None or not self.app.debug:
_request_ctx_stack.pop()
就是用来存储上下文的,必须用with
打开
所以:self.request_context
主要是利用stack保证在一个线程里处理这个请求
6.接着看preprocess_request
def preprocess_request(self):
for func in self.before_request_funcs:
rv = func()
if rv is not None:
return rv
对请求先进行预处理
7.dispatch_request
对request
进行分发处理
def dispatch_request(self):
try:
endpoint, values = self.match_request()
return self.view_functions[endpoint](**values)
except HTTPException, e:
handler = self.error_handlers.get(e.code)
if handler is None:
return e
return handler(e)
except Exception, e:
handler = self.error_handlers.get(500)
if self.debug or handler is None:
raise
return handler(e)
返回处理函数的处理结果,就是在flask
里写的
@app.route('/')
def hello_world():
return 'Hello, World!'
8.self.make_response
将结果封装成response
,有的在上一步中已经封装了
def make_response(self, rv):
if isinstance(rv, self.response_class):
return rv
if isinstance(rv, basestring):
return self.response_class(rv)
if isinstance(rv, tuple):
return self.response_class(*rv)
return self.response_class.force_type(rv, request.environ)
9.process_response(self, response)
对response进行最后一步处理
def process_response(self, response):
session = _request_ctx_stack.top.session
if session is not None:
self.save_session(session, response)
for handler in self.after_request_funcs:
response = handler(response)
return response
参考:
https://cizixs.com/2017/01/11/flask-insight-start-process/
https://www.toptal.com/python/pythons-wsgi-server-application-interface