Falsk框架session请求流程
from flask import Flask
# 1. 实例化Flask对象
app = Flask(__name__)# 2. 设置路由
@app.route('/index')
def index():
return "index"if __name__ == '__main__':
# 3. 启动socket服务端
app.run()
# 4. 用户请求到来
app.__call__
app.wsgi_app
app.request_class
app.session_interface
请求进来之后走run,run最后执行的是run_simple(host, port, self, **options)
####### app.py 文件下的 class Flask(_PackageBoundObject) 下的 def run ######
### 最后执行了run_simple(host, port, self, **options)
def run(self, host=None, port=None, debug=None,
load_dotenv=True, **options):
# Change this into a no-op if the server is invoked from the
# command line. Have a look at cli.py for more information.
if os.environ.get('FLASK_RUN_FROM_CLI') == 'true':
from .debughelpers import explain_ignored_app_run
explain_ignored_app_run()
returnif get_load_dotenv(load_dotenv):
cli.load_dotenv()# if set, let env vars override previous values
if 'FLASK_ENV' in os.environ:
self.env = get_env()
self.debug = get_debug_flag()
elif 'FLASK_DEBUG' in os.environ:
self.debug = get_debug_flag()# debug passed to method overrides all other sources
if debug is not None:
self.debug = bool(debug)_host = '127.0.0.1'
_port = 5000
server_name = self.config.get('SERVER_NAME')
sn_host, sn_port = None, Noneif server_name:
sn_host, _, sn_port = server_name.partition(':')host = host or sn_host or _host
port = int(port or sn_port or _port)options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
options.setdefault('threaded', True)cli.show_server_banner(self.env, self.debug, self.name, False)
from werkzeug.serving import run_simple
try:
run_simple(host, port, self, **options)
finally:
# reset the first request information if the development server
# reset normally. This makes it possible to restart the server
# without reloader and that stuff from an interactive shell.
self._got_first_request = False
werkzeug源码讲到,执行run_simple 方法,其实就是 当请求来时 最后会调用第三个参数加括号执行,即执行self的 __call__ 方法
参考:https://blog.csdn.net/fenglepeng/article/details/104676817
请求到来
当请求到来,执行__call__方法。看一下源码。
####### app.py 文件下的 class Flask(_PackageBoundObject) 下的 def __call__ ######
## 最后执行self.wsgi_app(environ, start_response)
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
# environ:请求相关的所有数据,wsgi将原生的请求做第一步处理,把字符串分割。(wsgi做了初步封装)
# start_response:用于设置响应相关的所有数据。
return self.wsgi_app(environ, start_response)
点开 wsgi_app
####### app.py 文件下的 class Flask(_PackageBoundObject) 下的 def wsgi_app ######
# 这个文件是Flask的整个执行流程的入口
def wsgi_app(self, environ, start_response):
'''
1、获取environ并对其进行再次封装。就成了我们要的request;并获取session值,此时为空
两个东西放到“某个神奇”的地方。ctx.request = Request(environ) ctx.session = None
'''
ctx = self.request_context(environ) # 实际执行ctx = RequestContext(self, environ)
error = None
try:
try:
# 2、执行SecureCookieSessionInterface.open_session(),去cookie中获取session的值,反序列化解密之后给ctx.session重新赋值(默认session的值存在cookie中)。
ctx.push()# 3、执行视图函数,然后去‘某个神奇’的地方获取session,加密,序列化,写入cookie
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None# 4、“某个神奇”的地方位置清空 (请求结束)
ctx.auto_pop(error)
1 、首先查看 request_context 函数
####### app.py 文件下的 class Flask(_PackageBoundObject) 下的 def request_context ######
def request_context(self, environ): # self 是app,即Flask的实例化
return RequestContext(self, environ)
RequestContext把我们的对象和请求封装到了一个类。我们对这个类进行实例化,看一下做了什么事?
####### ctx.py 文件下的 class RequestContext(Object) 下的 def __init__ ######
def __init__(self, app, environ, request=None):
self.app = app
if request is None:
# 对environ进行第二次封装,封装成一个Request对象
request = app.request_class(environ) # request_class = Request 实际执行为 request = Request(environ)# 把 request 返回
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.flashes = None
# 为session 赋值 None
self.session = Noneself._implicit_app_ctx_stack = []
self.preserved = False
self._preserved_exc = None
self._after_request_functions = []
self.match_request()
回到wsgi_app类中,我们会得到:
- ctx.request=Request(environ)。environ是一个原始的请求对象,但是现在被Request包裹,就不是原始的了。我们就可以执行".args",".form",".method"等。会自动帮我们去原始的数据解析。
- ctx.session=None.
现在ctx已经封装了两个值了,一个是请求相关的数据,一个是session,把它放到“某个神奇的地方”。
视图函数:
2、点开ctx.push()。上面的可以先省略
####### ctx.py 文件下的 class RequestContext(Object) 下的 def push ######
### 前面先不看,直接看if self.session is None:
def push(self):
top = _request_ctx_stack.top
if top is not None and top.preserved:
top.pop(top._preserved_exc)# Before we push the request context we have to ensure that there
# is an application context.
app_ctx = _app_ctx_stack.top
if app_ctx is None or app_ctx.app != self.app:
app_ctx = self.app.app_context()
app_ctx.push()
self._implicit_app_ctx_stack.append(app_ctx)
else:
self._implicit_app_ctx_stack.append(None)if hasattr(sys, 'exc_clear'):
sys.exc_clear()_request_ctx_stack.push(self)
# 这里这里
# 当请求进来时,session 肯定为空,因为上面设置的是空。Flask的session加密,序列化之后保存在cookie中
if self.session is None:
# session_interface.open_session 相当于执行 SecureCookieSessionInterface.open_session
session_interface = self.app.session_interface
self.session = session_interface.open_session(
self.app, self.request
)if self.session is None:
self.session = session_interface.make_null_session(self.app)
点击进入 session_interface
####### app.py 文件下的 class Flask(_PackageBoundObject) 下的 def request_context ######
class Flask(_PackageBoundObject)
session_interface = SecureCookieSessionInterface()
查看open_session
## sessions.py 文件下的 class SecureCookieSessionInterface(SessionInterface) 下的 def open_session ######
# 在cookie中取出session的key,然后获取对应的session值,并返回
def open_session(self, app, request):
s = self.get_signing_serializer(app) # 加密
if s is None:
return None
val = request.cookies.get(app.session_cookie_name) # 去cookie中取值
if not val: # 第一次访问为空执行
return self.session_class() # 返回{}
max_age = total_seconds(app.permanent_session_lifetime)
try:
data = s.loads(val, max_age=max_age) # loads:反序列化 val:原来的值
return self.session_class(data) # {"k1":123}
except BadSignature:
return self.session_class()
open_session返回啥self.session中就是啥。
现在回到我们的wsgi_app类中,ctx.push() 的作用:执行SecureCookieSessionInterface.open_session(),去cookie中获取值,并反序列化解密之后给ctx.session重新赋值。
3、现在才开始真正走视图函数full_dispatch_request
######################## app.py 文件下的 class Flask 下的 full_dispatch_request ####################
def full_dispatch_request(self):
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request() # 获取request
if rv is None:
rv = self.dispatch_request() # 调用视图函数
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv) # 视图函数执行完毕的善后工作
点击进入 finalize_request
######################## app.py 文件下的 class Flask 下的 finalize_request ####################
def finalize_request(self, rv, from_error_handler=False):response = self.make_response(rv)
try:
response = self.process_response(response) # 触发函数
request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception('Request finalizing failed with an '
'error while handling an error')
return response
点击进入 process_response
######################## app.py 文件下的 class Flask 下的 process_response ####################
def process_response(self, response):
ctx = _request_ctx_stack.top
bp = ctx.request.blueprint
funcs = ctx._after_request_functions
if bp is not None and bp in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
if None in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[None]))
for handler in funcs:
response = handler(response)
if not self.session_interface.is_null_session(ctx.session):
self.session_interface.save_session(self, ctx.session, response) # 保存session
return response
看一下save_session:
def save_session(self, app, session, response):
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)# If the session is modified to be empty, remove the cookie.
# If the session is empty, return without setting the cookie.
if not session:
if session.modified:
response.delete_cookie(
app.session_cookie_name,
domain=domain,
path=path
)return
# Add a "Vary: Cookie" header if the session was accessed at all.
if session.accessed:
response.vary.add('Cookie')if not self.should_set_cookie(app, session):
returnhttponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
samesite = self.get_cookie_samesite(app)
expires = self.get_expiration_time(app, session)# 前面不看,暂时用不到
val = self.get_signing_serializer(app).dumps(dict(session)) # 加密序列化成字符串
response.set_cookie( # 设置cookie
app.session_cookie_name,
val,
expires=expires,
httponly=httponly,
domain=domain,
path=path,
secure=secure,
samesite=samesite
)
最后执行 ctx.auto_pop(error)
这就是Flask框架中sesion的请求流程。说了这么多,其实真正实现咱们想要的功能的就是两个方法:open_session,save_session.请求进来执行open_session,请求走的时候执行save_session。
默认都是调用app.session_interface。
流程:
请求到来:请求到来之后wsgi会触发__call__方法,由__call__方法再次调用wsgi_app方法,将请求和session相关封装到ctx = RequestContext对象中,此时session为空,request二次封装,可以使用将app和g封装到app_ctx = AppContext对象中。再通过LocalStack对象将ctx、app_ctx封装到Local对象中。
获取数据:
通过LocalProxy对象+偏函数,调用LocalStack去Local中获取响应ctx、app_ctx中封装的值。
请求结束:
调用LocalStack的pop方法,将ctx和app_ctx移除。