首先请求进来先执行
app.run(debug=True)
进入run方法中
from werkzeug.serving import run_simple if host is None: host = '127.0.0.1' if port is None: server_name = self.config['SERVER_NAME'] if server_name and ':' in server_name: port = int(server_name.rsplit(':', 1)[1]) else: port = 5000 if debug is not None: self.debug = bool(debug) options.setdefault('use_reloader', self.debug) options.setdefault('use_debugger', self.debug) 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
它会执行run_simple() 方法,我们知道run_simple函数会对self加括号执行,那么self是谁呢,就是Flask的实例化对象app,给对象加(),相当于执行对象的__call__方法,我们只能从Flask的类中找__call__
def __call__(self, environ, start_response): """Shortcut for :attr:`wsgi_app`.""" return self.wsgi_app(environ, start_response)
我们可以看到他把environ和start_response传到wsgi_app()中,我们进入到wsgi_app()方法中
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) #封装了request和session,并把environ处理成了request,session=None,其实这里还有路由匹配 ctx.push()#1.把ctx放到local对象中保存起来,给self.session赋值 2.app_ctx封装了app和g,并把app_ctx放到另个一个local对象中去 error = None try: try: 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 ctx.auto_pop(error) wsgi_app
我们慢慢的分析该函数的代码,先看第一句
ctx = self.request_context(environ)
进入到request_context中
def request_context(self, environ): return RequestContext(self, environ) #这里的self是app
我们可以看到该函数返回一个实例化对象并把request,和session传进去了,这里我们要分清self就是app,
也就是说ctx就是RequestContex实例化的对象,注意ctc中还有我们的app对象,也就是说ctc可以调出app下的方法
我们进去看一下,我们只看该类的init方法
def __init__(self, app, environ, request=None): self.app = app if request is None: request = app.request_class(environ) #把environ处理成request self.request = request self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None
我们可以看到,该类实例化方法把environ加工处理封装给了request对象了.其实request是Request的一个对象,这个类下含有好多的方法.
注意ctx中还有我们的app对象,也就是说ctc可以调出app下的方法,还有一点我们要注意到,
还有:self.session=None
我们接着来看wsgi_app中的第二句代码
ctx.push()
我们进去push方法
def push(self): top = _request_ctx_stack.top if top is not None and top.preserved: top.pop(top._preserved_exc) app_ctx = _app_ctx_stack.top #2.3去localStack中的local对象中拿app_ctx,主意这里的LocalStack和_request_ctx_stack中的LocalStack不是一个对象 if app_ctx is None or app_ctx.app != self.app:#2.4如果localStack中没有当前的app_ctx app_ctx = self.app.app_context()#2.5 就自己创建一个app_ctx,其中封装了app和g app_ctx.push()#2.6 #把新创建的app_ctx放到local对象中 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)#这里就是把ctx对象放入到local中 #2.1 self.session = self.app.open_session(self.request) #给self.session赋值 #2.2 if self.session is None: self.session = self.app.make_null_session() push方法
我们来看2.1关键代码
_request_ctx_stack.push(self)
先进入_request_ctx_stack中我们看到,但是注意这里的self是ctx
_request_ctx_stack = LocalStack() #
原来是一个实例化对象,我们继续进入Localstack中()
我们要注意这句,类里面又实例化了一个对象
def __init__(self): self._local = Local()
进入Local类中我们看到
class Local(object): __slots__ = ('__storage__', '__ident_func__') #限制类的属性只能有这俩 def __init__(self): object.__setattr__(self, '__storage__', {}) #生成一个字典 object.__setattr__(self, '__ident_func__', get_ident) def __iter__(self): return iter(self.__storage__.items()) def __call__(self, proxy): """Create a proxy for a name.""" return LocalProxy(self, proxy) def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name)
可以看到这和我们写的local对象中第三种代码一样,这里就是用来存放每个线程中的ctx的
下面我们来看Localstack对象中的push方法
def push(self, obj): """Pushes a new item to the stack""" rv = getattr(self._local, 'stack', None) #self._local就是local对象,查看对象中有没有stack属性 if rv is None:#如果没有就会调用__setattr__方法#2.1.1 self._local.stack = rv = [] rv.append(obj)#把ctx放入到列表中 return rv
这里我们来看这句代码
if rv is None: self._local.stack = rv = []
其实就是执行__setattr__方法
def __setattr__(self, name, value): ident = self.__ident_func__() #唯一标识 storage = self.__storage__ #storage={} try: storage[ident][name] = value #不会走这里,因为第一次进来没有ident更没有name,会出现keyValue except KeyError: storage[ident] = {name: value} # 会走这里
上把您的结果:
storage={ident:{“stack”:[ctx ]}}
_request_ctx_stack.push的主要作用就是把ctc实例对象放到local对象的变量字典中
我们看2.2关键代码 给session赋值
self.session = self.app.open_session(self.request) #Flask类中的open_session
我们先进入到open_session中
def open_session(self, request): return self.session_interface.open_session(self, request)
继续进入
session_interface = SecureCookieSessionInterface()
继续进入
def open_session(self, app, request): s = self.get_signing_serializer(app)#这里直接判断有没有设置screct_key if s is None: return None val = request.cookies.get(app.session_cookie_name) if not val: return self.session_class() max_age = total_seconds(app.permanent_session_lifetime) try: data = s.loads(val, max_age=max_age) return self.session_class(data) except BadSignature: return self.session_class()
我们直接看这句代码,主要是先看有没有设置secret_key: 如果没有直接返回None,所以必须设置secret_key的值
s = self.get_signing_serializer(app)
我们接着往下看
val = request.cookies.get(app.session_cookie_name) if not val: return self.session_class()
我们知道第一次进来cookie中肯定没值 ,所以val=None,我们就直接进入到session_class中
session_class = SecureCookieSession
竟然是实例化了一个对象,
class SecureCookieSession(CallbackDict, SessionMixin): class CallbackDict(UpdateDictMixin, dict):
我们继续进入就可以了解到这个对象是一个类似字典的一个类型
也就是如果是请求第一次进入的话self.session=实例化了一个类似字典的一个对象,
但是当你第二次进来的时候这个字典里就有我们设置的值了.
如果你没有设置secret_key,nameself.session=None就会执行push方法中的这个代码
if self.session is None: self.session = self.app.make_null_session() #如果没有设置sesret_kek就会抛出错误
我们接着来看wsgi_app中的第三句代码
response = self.full_dispatch_request()
该函数主要是用来执行视图函数的.
进入该函数
def full_dispatch_request(self): 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) #视图函数中出现错误 return self.finalize_request(rv) #
我们来看最重要的self.disptch_request
def dispatch_request(self): req = _request_ctx_stack.top.request# 从local对象中取出ctx中的request if req.routing_exception is not None: self.raise_routing_exception(req) rule = req.url_rule # # if we provide automatic options for this URL and the # request came with the OPTIONS method, reply automatically if getattr(rule, 'provide_automatic_options', False) \ and req.method == 'OPTIONS':# return self.make_default_options_response()# # otherwise dispatch to the handler for that endpoint return self.view_functions[rule.endpoint](**req.view_args)#
req = _request_ctx_stack.top.request# 从local对象中取出ctx中的request,
我们进入到 req.url_rule 中,
看不懂了
---------------------------------------------------------------------------------------在视图函数中导入reques和导入session-----------------------------------------------------------------------
在视图函数中导入request时
from Flask import request
这句话经历了哪些源码
request = LocalProxy(partial(_lookup_req_object, 'request'))
其中partial是偏函数作用于__lookup_req_object函数上减少其参数的输入,这里就代表着__lookup_req_object,函数不用传参数,request参数已经传好了,上边这句话的代码等价于
request = LocalProxy(_lookup_req_object)#把该函数的函数名当参数传递给LocalProxy类
进入LocalProxy类中,
def __init__(self, local, name=None): object.__setattr__(self, '_LocalProxy__local', local)#这里相当于self.__local=local #local就是__lookup_req_obj函数名,至于为什么不是self.__LocalProxy__local ,因为
self.__local是私有属性,我们在外部不能调,但是如果真的想调用的话,这样调self.__类名__local就可以,因为这里要用到object中的私有属性
object.__setattr__(self, '__name__', name)#self.__name__=name
我们先只看这些代码,讲的是LocalProxy的初始化
当我们在视图函数中写入
m=request.method
经历了哪些源码,首先触发了LocalProxy中的__getattr__
def __getattr__(self, name):#name就是method if name == '__members__': #判断是不是request.__members return dir(self._get_current_object()) return getattr(self._get_current_object(), name)
我们进入到self.__get_current_object中
def _get_current_object(self): if not hasattr(self.__local, '__release_local__'): return self.__local() #这里返回___lookup_req_object函数的执行结果,其中request的参数已经省略 try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError('no object bound to %s' % self.__name__)
我们进入到_lookup_req_object函数中,
def _lookup_req_object(name): top = _request_ctx_stack.top #name=request 返回ctxraise RuntimeError(_request_ctx_err_msg) return getattr(top, name) #等价于:getattr(ctx,'request')
其中
top = _request_ctx_stack.top #其中_request_ctx_stack = LocalStack()
会调用
Localstack类中的top方法
def top(self): try: return self._local.stack[-1] #其中__local=local except (AttributeError, IndexError): return None
其中
return self._local.stack[-1]
回调用local中的__getattr__方法
def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] #name=stark except KeyError: raise AttributeError(name)
该函数返回存储在local中该线程的ctx对象
也就是
top = _request_ctx_stack.top #top等于ctx
那么:
getattr(top, name) #即 getattr(ctx, 'request') 结果就是request对象啊
即 _lookup_req_object函数 返回的就是request的对象,
则self.__get_current_object函数返回的结果就是request对象
那么刚开始的LocalProxy中的__getattr__中
getattr(self._get_current_object(), name)
等于
getattr(request, 'method')
这样request.method中的东西也就取出来.
在视图函数中导入session和导入request的原理差不多,就不在写
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Flask_session源码
from flask_session import Session
app = Flask(__name__)
Session(app)
上边是我简单写的:
进入到Session类中
def __init__(self, app=None): self.app = app if app is not None: self.init_app(app) #1.1
进入 init_app中
def init_app(self, app): """This is used to set up session for your app object. :param app: the Flask app object with proper configuration. """ app.session_interface = self._get_interface(app)
继续进入 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
上边就是可以配置的所有类型的数据库,
我们来看redis的
if config['SESSION_TYPE'] == 'redis': session_interface = RedisSessionInterface( config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
进入到 RedisSessionInterface类中
我们来看其中的open_session
def open_session(self, app, request): sid = request.cookies.get(app.session_cookie_name)#从cooke中拿sid if not sid:#第一次登陆 sid = self._generate_sid() #1.1生成sid return self.session_class(sid=sid, permanent=self.permanent)#1.2 if self.use_signer:#如果有签名 signer = self._get_signer(app) if signer is None: return None try: sid_as_bytes = signer.unsign(sid) sid = sid_as_bytes.decode() except BadSignature: sid = self._generate_sid() return self.session_class(sid=sid, permanent=self.permanent) if not PY2 and not isinstance(sid, text_type):# sid = sid.decode('utf-8', 'strict') val = self.redis.get(self.key_prefix + sid)#1.3从redis中根据key_prefix+sid拿数据 if val is not None: try: data = self.serializer.loads(val) #1.4用到了pickle的反序列haul return self.session_class(data, sid=sid)#1.5#把data和数据传给了RedisSession对象 except: return self.session_class(sid=sid, permanent=self.permanent) return self.session_class(sid=sid, permanent=self.permanent)#
第一次登陆没有值:
#1.1 sid=_generate_sid()
def _generate_sid(self): return str(uuid4())
返回一个uuid4的字符串
#1.2self.session_class(sid=sid, permanent=self.permanent)#
进入得到:
返回一个类
session_class = RedisSession
进入:RedisSession类中
def __init__(self, initial=None, sid=None, permanent=None): def on_update(self): self.modified = True CallbackDict.__init__(self, initial, on_update) self.sid = sid if permanent: self.permanent = permanent self.modified = False
总结一点把sid 封装到了RedisSession类中
#1.3 val = self.redis.get(self.key_prefix + sid)#从redis中根据key_prefix+sid拿数据
如果redis中有值:
先把数据用pickle序列化,再把数据和sid传给RedisSession类.
----------------------------------------------------------------------------------
我们在看是如何保存把session保存到redis中和cookie中的
我们进入到Save_session中:
def save_session(self, app, session, response): domain = self.get_cookie_domain(app) #取cookie域名 path = self.get_cookie_path(app) #取cookie地址 if not session: #如果session为空就把redis中的session删除 if session.modified: self.redis.delete(self.key_prefix + session.sid) response.delete_cookie(app.session_cookie_name, domain=domain, path=path) return httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) expires = self.get_expiration_time(app, session) #过期时间 val = self.serializer.dumps(dict(session)) #用pickle序列化session self.redis.setex(name=self.key_prefix + session.sid, value=val, time=total_seconds(app.permanent_session_lifetime)) #2.1 把session存到redis中 if self.use_signer:#如果有签名 session_id = self._get_signer(app).sign(want_bytes(session.sid)) else: #如没有签名 session_id = session.sid response.set_cookie(app.session_cookie_name, session_id, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)#把session存到cookie中
我挑主要的说啊:
- 该函数把session的存到redis中以key_prefix当键,以val为值.
-
{key_prefix+sid:val}
-
- 该函数以session_cookie_name为键以sid为值存到了cookie中
Set-Cookie: session=0ac14b94-c60f-4096-915c-bff86503675d; HttpOnly; Path=/