flask-session
内置session原理
请求到来
""" 1. 请求刚刚达到 ctx = RequestContext(...) - request - session=None ctx.push() ctx.session = SecureCookieSessionInterface.open_session lku 2. 视图函数 3. 请求结束 SecureCookieSessionInterface.save_session() """ from flask import Flask,session app = Flask(__name__) app.secret_key = 'sadfasdfasdf' @app.route('/x1') def index(): # 去ctx中获取session session['k1'] = 123 session['k2'] = 123 del session['k2'] return "Index" @app.route('/x2') def order(): print(session['k1']) return "Order" if __name__ == '__main__': app.run() # 1. 请求一旦到来显 app.__call__ app.wsgi_app app.open_session
当请求进来之后,先执行Flask对象的 __call__ 方法
def wsgi_app(self, environ, start_response):
# 获取请求相关数据,并进行封装和加工
ctx = self.request_context(environ)
# 将请求消息推送到堆栈中,并执行 open_session方法
ctx.push()
error = None
try:
try:
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.make_response(self.handle_exception(e))
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`."""
return self.wsgi_app(environ, start_response)
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)
# 调用Flask对象的open_session方法
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session()
def open_session(self, request):
"""Creates or opens a new session. Default implementation stores all
session data in a signed cookie. This requires that the
:attr:`secret_key` is set. Instead of overriding this method
we recommend replacing the :class:`session_interface`.
:param request: an instance of :attr:`request_class`.
"""
# self指的是Flask对象,session_interface默认值为SecureCookieSessionInterface()
return self.session_interface.open_session(self, request)
由以上源码发现,当接收到用户请求之后,会调用 Flask对象的 session_interface对象的open_session方法,以此来获取一个session对象。
class SecureCookieSessionInterface(SessionInterface):
"""The default session interface that stores sessions in signed cookies
through the :mod:`itsdangerous` module.
"""
#: the salt that should be applied on top of the secret key for the
#: signing of cookie based sessions.
salt = 'cookie-session'
#: the hash function to use for the signature. The default is sha1
digest_method = staticmethod(hashlib.sha1)
#: the name of the itsdangerous supported key derivation. The default
#: is hmac.
key_derivation = 'hmac'
#: A python serializer for the payload. The default is a compact
#: JSON derived serializer with support for some extra Python types
#: such as datetime objects or tuples.
serializer = session_json_serializer
session_class = SecureCookieSession
def get_signing_serializer(self, app):
if not app.secret_key:
return None
signer_kwargs = dict(
key_derivation=self.key_derivation,
digest_method=self.digest_method
)
return URLSafeTimedSerializer(app.secret_key, salt=self.salt,
serializer=self.serializer,
signer_kwargs=signer_kwargs)
def open_session(self, app, request):
# 获取加密相关的类,必须设置app.secret_key,不然s就是None
s = self.get_signing_serializer(app)
if s is None:
return None
# 去Cookie中获取 session 对应的值(该值默认是加密之后的session的值,也可以改造成随机字符串)
val = request.cookies.get(app.session_cookie_name)
if not val:
# 未获取到值,则创建一个空字典(就是flask中用到的session)
return self.session_class()
max_age = total_seconds(app.permanent_session_lifetime)
try:
data = s.loads(val, max_age=max_age)
# 如果获取到值,则将值放入字典中(就是flask中用到的session)
return self.session_class(data)
except BadSignature:
# 解密失败,则创建一个空字典(就是flask中用到的session)
return self.session_class()
上述中 self.session_class 就是创建的一个SecureCookieSession对象,这个类是继承了字典的类,其实就是一个特殊的字典。
class SessionMixin(object):
"""Expands a basic dictionary with an accessors that are expected
by Flask extensions and users for the session.
"""
def _get_permanent(self):
return self.get('_permanent', False)
def _set_permanent(self, value):
self['_permanent'] = bool(value)
#: this reflects the ``'_permanent'`` key in the dict.
permanent = property(_get_permanent, _set_permanent)
del _get_permanent, _set_permanent
#: some session backends can tell you if a session is new, but that is
#: not necessarily guaranteed. Use with caution. The default mixin
#: implementation just hardcodes ``False`` in.
new = False
#: for some backends this will always be ``True``, but some backends will
#: default this to false and detect changes in the dictionary for as
#: long as changes do not happen on mutable structures in the session.
#: The default mixin implementation just hardcodes ``True`` in.
modified = True
class UpdateDictMixin(object):
"""Makes dicts call `self.on_update` on modifications.
.. versionadded:: 0.5
:private:
"""
on_update = None
def calls_update(name):
def oncall(self, *args, **kw):
rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw)
if self.on_update is not None:
self.on_update(self)
return rv
oncall.__name__ = name
return oncall
def setdefault(self, key, default=None):
modified = key not in self
rv = super(UpdateDictMixin, self).setdefault(key, default)
if modified and self.on_update is not None:
self.on_update(self)
return rv
def pop(self, key, default=_missing):
modified = key in self
if default is _missing:
rv = super(UpdateDictMixin, self).pop(key)
else:
rv = super(UpdateDictMixin, self).pop(key, default)
if modified and self.on_update is not None:
self.on_update(self)
return rv
__setitem__ = calls_update('__setitem__')
__delitem__ = calls_update('__delitem__')
clear = calls_update('clear')
popitem = calls_update('popitem')
update = calls_update('update')
del calls_update
class CallbackDict(UpdateDictMixin, dict):
"""A dict that calls a function passed every time something is changed.
The function is passed the dict instance.
"""
def __init__(self, initial=None, on_update=None):
dict.__init__(self, initial or ())
self.on_update = on_update
def __repr__(self):
return '<%s %s>' % (
self.__class__.__name__,
dict.__repr__(self)
)
class SecureCookieSession(CallbackDict, SessionMixin):
"""Base class for sessions based on signed cookies."""
def __init__(self, initial=None):
def on_update(self):
self.modified = True
CallbackDict.__init__(self, initial, on_update)
self.modified = False
该字典其实就是继承了字典,并在其基础上定制了一些功能,如
class MyDict(dict):
def __init__(self, initial):
dict.__init__(self, initial)
session = MyDict({'k1': 123})
print(session, type(session)) # {'k1': 123} <class '__main__.MyDict'>
session['k2'] = 'v2'
print(session)
所以,Flask的视图函数中在对session进行操作时,其实就是在内存中修改一个字典的数据。

浙公网安备 33010602011771号
业务处理
设置session
响应内容
响应内容其实就讲数据返回给用户,并且把内容中的session重新保存
def wsgi_app(self, environ, start_response): """The actual WSGI application. This is not implemented in `__call__` so that middlewares can be applied without losing a reference to the class. So instead of doing this:: app = MyMiddleware(app) It's a better idea to do this instead:: app.wsgi_app = MyMiddleware(app.wsgi_app) Then you still have the original application object around and can continue to call methods on it. .. versionchanged:: 0.7 The behavior of the before and after request callbacks was changed under error conditions and a new callback was added that will always execute at the end of the request, independent on if an error occurred or not. See :ref:`callbacks-and-errors`. :param environ: a WSGI environment :param start_response: a callable accepting a status code, a list of headers and an optional exception context to start the response """ ctx = self.request_context(environ) ctx.push() error = None try: try: # 处理业务请求,并获取返回值 response = self.full_dispatch_request() except Exception as e: error = e response = self.make_response(self.handle_exception(e)) return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)def full_dispatch_request(self): """Dispatches the request and on top of that performs request pre and postprocessing as well as HTTP exception catching and error handling. .. versionadded:: 0.7 """ self.try_trigger_before_first_request_functions() try: request_started.send(self) # 执行视图函数,处理业务请求 rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() except Exception as e: rv = self.handle_user_exception(e) response = self.make_response(rv) # 处理响应内容 response = self.process_response(response) request_finished.send(self, response=response) return responsedef process_response(self, response): """Can be overridden in order to modify the response object before it's sent to the WSGI server. By default this will call all the :meth:`after_request` decorated functions. .. versionchanged:: 0.5 As of Flask 0.5 the functions registered for after request execution are called in reverse order of registration. :param response: a :attr:`response_class` object. :return: a new response object or the same, has to be an instance of :attr:`response_class`. """ 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): # 执行flask对象的save_session方法 self.save_session(ctx.session, response) return response def save_session(self, session, response): """Saves the session if it needs updates. For the default implementation, check :meth:`open_session`. Instead of overriding this method we recommend replacing the :class:`session_interface`. :param session: the session to be saved (a :class:`~werkzeug.contrib.securecookie.SecureCookie` object) :param response: an instance of :attr:`response_class` """ # 执行session_interface的save_session方法,将内存中的session保存。 return self.session_interface.save_session(self, session, response)执行xxx的save_session方法,将内存中的数据保存。
待续: