Flask请求流程超清大图
补充一下
request是在哪里产生的:
class RequestContext(object): # app就是flask对象 self.app = app if request is None: request = app.request_class(environ) # Request(environ) 处理request self.request = request # 就是这里没错了 self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None
请求流程
session的存取过程
session的取值过程位于上图的此部分
Flask实例的open_session方法首先是取了session_interface的值--一个类(SecureCookieSessionInterface)的对象,调用了他的open_session方法,将取出来的值赋予了ctx.session,存储到了Local实例中
session的存值过程
则是调用了session_interface代表的类实例的save_session方法但是这里存在一个问题:
class SecureCookieSessionInterface(SessionInterface): session_class = SecureCookieSession def open_session(self, app, request): return self.session_class() # ctx的session就是这个类的实例 # 父类中的此方法,我给直接写到这里了,如果此函数返回False,save_session不会设置cookie def should_set_cookie(self, app, session): if session.modified: # 如果session.modified为True就说明session修改过了,为什么说修改过了,看下面 return True save_each = app.config['SESSION_REFRESH_EACH_REQUEST'] # session.permanent在SessionMixin类中 return save_each and session.permanent def save_session(self, app, session, response): # 保存session时会根据session的modified来判断将不将session写进cookie中,下面简写 if not self.should_set_cookie(app, session): return # session的类 class SecureCookieSession(CallbackDict, SessionMixin): def __init__(self, initial=None): def on_update(self): self.modified = True CallbackDict.__init__(self, initial, on_update) self.modified = False # 默认是False class CallbackDict(UpdateDictMixin, dict): def __init__(self, initial=None, on_update=None): dict.__init__(self, initial or ()) self.on_update = on_update # 这个就是上面init定义的方法 class UpdateDictMixin(object): # 这个类中定义了__setitem__和__delitem__ 会去执行self.on_update,修改self.modified的值 # 问题就出在这里,session["x"]=""才会去执行self.on_update, # 但是如果session["x"]对应的是个数据结构,修改这个数据结构中的元素并不会去执行elf.on_update, # session.modified就还是False,修不修改session就要看save_each and session.permanent了 # 如果他们之间有一个false就不会去修改,可以直接看到save_each存在与配置中 # ,而session.permanent在SessionMixin类中 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 # session.permanent相关的 class SessionMixin(object): # 取值时走这个方法 def _get_permanent(self): return self.get('_permanent', False) # 通过.设置值是走这个方法 def _set_permanent(self, value): self['_permanent'] = bool(value) # 这里不明白,为他设置值的时候会去调用on_update修改modified = True,如果设置了他 # SessionInterface中的should_set_cookie就会直接return True # 而如果没设置就直接return session.permanent就好了,为什么还要and save_each permanent = property(_get_permanent, _set_permanent) del _get_permanent, _set_permanent new = False modified = True
将session存在其他地方
要将session存在其他地方,主要是和Flask中的session_interface属性对应的类实例有关系的.这些类要提供save_session和open_session方法
Flask的flask_session组件就提供了这些类
安装
pip install flask_session
用法
# 配置类,具体的配置是根据数据库类型设置的 class Config(object): SECRET_KEY = "umsuldfsdflskjdf" # 加密用 PERMANENT_SESSION_LIFETIME = timedelta(minutes=20) SESSION_TYPE = "redis" SESSION_REDIS = Redis(host='192.168.0.94', port='6379') # 数据库连接,可以用连接池 from flask import Flask,session from flask_session import Session app = Flask(__name__) app.config.from_object('Config') Session(app)
Session干了什么
class Session(object): def __init__(self, app=None): self.app = app if app is not None: self.init_app(app) def init_app(self, app): # 为flase实例赋了新的实例 app.session_interface = self._get_interface(app) def _get_interface(self, app): # 根据配置来匹配不同的类 config = app.config.copy() config.setdefault('SESSION_TYPE', 'null') config.setdefault('SESSION_PERMANENT', True) config.setdefault('SESSION_USE_SIGNER', False) config.setdefault('SESSION_KEY_PREFIX', 'session:') config.setdefault('SESSION_REDIS', None) config.setdefault('SESSION_MEMCACHED', None) config.setdefault('SESSION_FILE_DIR', os.path.join(os.getcwd(), 'flask_session')) config.setdefault('SESSION_FILE_THRESHOLD', 500) config.setdefault('SESSION_FILE_MODE', 384) config.setdefault('SESSION_MONGODB', None) config.setdefault('SESSION_MONGODB_DB', 'flask_session') config.setdefault('SESSION_MONGODB_COLLECT', 'sessions') config.setdefault('SESSION_SQLALCHEMY', None) config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions') if config['SESSION_TYPE'] == 'redis': session_interface = RedisSessionInterface( config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'memcached': session_interface = MemcachedSessionInterface( config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'filesystem': session_interface = FileSystemSessionInterface( config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'], config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'mongodb': session_interface = MongoDBSessionInterface( config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'], config['SESSION_MONGODB_COLLECT'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'sqlalchemy': session_interface = SqlAlchemySessionInterface( app, config['SESSION_SQLALCHEMY'], config['SESSION_SQLALCHEMY_TABLE'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) else: session_interface = NullSessionInterface() return session_interface