启动先执行manage.py 中的    app.run()

class Flask(_PackageBoundObject):
   def run(self, host=None, port=None, debug=None, **options):
  from werkzeug.serving import run_simple
  try:
  #run_simple 是werkzeug 提供的方法,会执行第三个参数 self()
  run_simple(host, port, self, **options)

执行app(),对象()表示调用对象的__call__方法

class Flask(_PackageBoundObject):
   def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)

又调用了app.wsgi_app方法

class Flask(_PackageBoundObject):
   def wsgi_app(self, environ, start_response):
#1.
     ctx = self.request_context(environ)
     #self.request_context
#2.
ctx.push()
     try:
try:
          #3.执行视图函数
response = self.full_dispatch_request()
except Exception as e:
error = e
          #4.
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
       #5.
ctx.auto_pop(error)

第1步:执行app.request_context方法,把请求的相关信息传进去了

class Flask(_PackageBoundObject):
   def request_context(self, environ):
return RequestContext(self, environ)

返回了一个RequestContext类的实例对象

class RequestContext(object):
   def __init__(self, app, environ, request=None):
self.app = app
if request is None:
request = app.request_class(environ)
       #app.request_class = Request
self.request = request
self.session = None

在init构造方法中注意app又调用了request_class方法,也就是Request 实例一个对象,

那么第1步我们知道:

ctx是一个RequestContext对象,这个对象里面封装了两个主要的属性,一个是self.request = Request实例的对象,Request对象里面封装了请求进来的所有数据;
另外一个是self.session = None就可以了

第2步:执行ctx.push()方法

因为ctx是RequestContext类的对象,那我们就要去RequestContext类中找push方法

class RequestContext(object):
   def push(self):
     #2.1.
app_ctx = _app_ctx_stack.top
if app_ctx is None or app_ctx.app != self.app:
app_ctx = self.app.app_context()
            # self.app.app_context = app.app_context = AppContext(app)
app_ctx.push()
     #2.2.
     _request_ctx_stack.push(self)
        #_request_ctx_stack = LocalStack()
     #2.3.
        self.session = self.app.open_session(self.request)         #判断没有 secret_key时:
        if self.session is None:
            self.session = self.app.make_null_session()
            #raise RuntimeError('The session is unavailable because no secret ''key was set.)

第2.1步:到_app_ctx_stack这个栈中取最后一个数据,如果未取到或者取到的不是当前的app,就调用app.app_context()方法,就是新实例一个上下文app_ctx对象,再执行app_ctx.push()方法     (在这再次强调,因为app_ctx是AppContext对象,就要先去AppContext类中找push方法),

class AppContext(object):
   def push(self):
_app_ctx_stack.push(self) #把新创建的app_ctx上下文app对象添加到了_app_ctx_stack这个栈中
appcontext_pushed.send(self.app) #在这里遇到了第一个信号,请求app上下文push时执行

第2.2步:LocalStack类的对象调用push方法

class LocalStack(object):
   def push(self, obj):
rv = getattr(self._local, 'stack', None) #self._local = Local()
     #第一次的时候rv肯定是None
if rv is None:
self._local.stack = rv = [] #Local对象 .stack = rv = [] 就执行了对象的 __setattr__方法
rv.append(obj) #把 ctx对象添加到Local类的列表中
return rv
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident class Local(object):
    def __init__(self):
        object.__setattr__(self, '__storage__', {}) #这里为什么用object.__setattr__ 而不是直接用self.__storage__={}
        object.__setattr__(self, '__ident_func__', get_ident) #如果用self的方式设置属性,就会触发self的__setattr__方法,就会无限的循环
  
def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value # {"唯一标识1":{"stack":[]},"唯一标识2":{"stack":[]}} 和本地线程类似
except KeyError:
storage[ident] = {name: value}

第2.3步:给ctx.session赋值,执行app.open_session(ctx.request)

class Flask(_PackageBoundObject):
   def open_session(self, request):
return self.session_interface.open_session(self, request)
     #return SecureCookieSessionInterface().open_session(app, request)
     #所以就要去SecureCookieSessionInterface类找open_session方法
class SecureCookieSessionInterface(SessionInterface):
   def open_session(self, app, request):
# 查看 是否有secret_key
s = self.get_signing_serializer(app)
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) #loads 作用是: 反序列化+解析乱码
return self.session_class(data) ##返回了一个 类似字典对象,对象里面有data
except BadSignature:
return self.session_class()

那么第2步我们知道:

1.把app_ctx上下文对象添加到了_app_ctx_stack这个栈中
2.把 ctx请求对象添加到Local类的列表中
3.执行open_session方法,把session加载到内

第3步:app.full_dispatch_request()   执行视图函数

class Flask(_PackageBoundObject):
    def full_dispatch_request(self):
        #3.1
        self.try_trigger_before_first_request_functions()
        try:
            request_started.send(self)     # 信号 - 请求到来前执行
            # 3.2
            rv = self.preprocess_request()
            if rv is None:
                # 3.3 如果所有的中间件都通过了, 执行视图函数
                rv = self.dispatch_request()
     #3.4
        return self.finalize_request(rv)

第3.1步:找到所有的 执行一次的 伪中间件 执行

class Flask(_PackageBoundObject):
def try_trigger_before_first_request_functions(self): with self._before_request_lock:
for func in self.before_first_request_funcs:
func()

第3.2步:找到所有的 伪中间件的执行

class Flask(_PackageBoundObject):
def preprocess_request(self): funcs = self.before_request_funcs.get(None, ())
for func in funcs:
rv = func()
if rv is not None:
return rv

第3.3步:

class Flask(_PackageBoundObject):
def dispatch_request(self):
#获取请求的ctx对象中的request数据
req = _request_ctx_stack.top.request
#获取请求的url
rule = req.url_rule
#执行视图函数
return self.view_functions[rule.endpoint](**req.view_args)

第3.4步:

class Flask(_PackageBoundObject):
def finalize_request(self, rv, from_error_handler=False):
response = self.make_response(rv) #通过make_response方法后就可以对返回值进行设置响应头等数据了
try:
       #3.4.1
response = self.process_response(response)
request_finished.send(self, response=response) #信号 - 请求结束后执行
return response

第3.4.1步:

class Flask(_PackageBoundObject):
def process_response(self, response):
ctx = _request_ctx_stack.top
#找到所有的 after_request 伪中间件执行
funcs = ctx._after_request_functions
for handler in funcs:
response = handler(response)
# 3.4.1.1 如果有session就执行self.save_session方法
if not self.session_interface.is_null_session(ctx.session):
     # self.session_interface = SecureCookieSessionInterface()
       #3.4.1.2
        self.save_session(ctx.session, response) return response

第3.4.1.1步: 到SecureCookieSessionInterface类中找is_null_session方法,发现没有,就去它基类SessionInterface中找

class SessionInterface(object):
def is_null_session(self, obj):
#判断ctx.session 是不是 self.null_session_class = NullSession 类或者它派生类的对象
return isinstance(obj, self.null_session_class)

第3.4.1.2步:执行了SecureCookieSessionInterface类的save_session方法

class Flask(_PackageBoundObject):
def save_session(self, session, response):
return self.session_interface.save_session(self, session, response)
# return SecureCookieSessionInterface().save_session(self, session, response)
class SecureCookieSessionInterface(SessionInterface):
def save_session(self, app, session, response):
#给响应设置cookie
response.set_cookie(app.session_cookie_name, val,
expires=expires, httponly=httponly,
domain=domain, path=path, secure=secure)

补充:自定义session

from flask import Flask,request,session
app = Flask(__name__)
app.secret_key = 'sdfsdfsd'
from flask.sessions import SessionInterface,SessionMixin
import uuid
import json
from flask.sessions import SessionInterface
from flask.sessions import SessionMixin
from itsdangerous import Signer, BadSignature, want_bytes class MySession(dict, SessionMixin):
def __init__(self, initial=None, sid=None):
self.sid = sid
self.initial = initial
super(MySession, self).__init__(initial or ()) def __setitem__(self, key, value):
super(MySession, self).__setitem__(key, value) def __getitem__(self, item):
return super(MySession, self).__getitem__(item) def __delitem__(self, key):
super(MySession, self).__delitem__(key) class MySessionInterface(SessionInterface):
session_class = MySession
container = {
# 'asdfasdfasdfas':{'k1':'v1','k2':'v2'}
# 'asdfasdfasdfas':"{'k1':'v1','k2':'v2'}"
} def __init__(self):
pass
# import redis
# self.redis = redis.Redis() def _generate_sid(self):
return str(uuid.uuid4()) def _get_signer(self, app):
if not app.secret_key:
return None
return Signer(app.secret_key, salt='flask-session',
key_derivation='hmac') def open_session(self, app, request):
"""
程序刚启动时执行,需要返回一个session对象
"""
sid = request.cookies.get(app.session_cookie_name)
if not sid:
# 生成随机字符串,并将随机字符串添加到 session对象中
sid = self._generate_sid()
return self.session_class(sid=sid) signer = self._get_signer(app)
try:
sid_as_bytes = signer.unsign(sid)
sid = sid_as_bytes.decode()
except BadSignature:
sid = self._generate_sid()
return self.session_class(sid=sid) # session保存在redis中
# val = self.redis.get(sid)
# session保存在内存中
val = self.container.get(sid) if val is not None:
try:
data = json.loads(val)
return self.session_class(data, sid=sid)
except:
return self.session_class(sid=sid)
return self.session_class(sid=sid) def save_session(self, app, session, response):
"""
程序结束前执行,可以保存session中所有的值
如:
保存到resit
写入到用户cookie
"""
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
expires = self.get_expiration_time(app, session) val = json.dumps(dict(session)) # session保存在redis中
# self.redis.setex(name=session.sid, value=val, time=app.permanent_session_lifetime)
# session保存在内存中
self.container.setdefault(session.sid, val) session_id = self._get_signer(app).sign(want_bytes(session.sid)) response.set_cookie(app.session_cookie_name, session_id,
expires=expires, httponly=httponly,
domain=domain, path=path, secure=secure) app.session_interface = MySessionInterface()
# app.session_interface = Foo()
# app.session_interface
# app.make_null_session()
@app.route('/index')
def index():
print('网站的所有session',MySessionInterface.container)
print(session)
session['k1'] = 'v1'
session['k2'] = 'v2'
del session['k1'] # 在内存中操作字典....
# session['k1'] = 'v1'
# session['k2'] = 'v2'
# del session['k1'] return "xx" if __name__ == '__main__':
app.__call__
app.run()

自定义类似django的session

第4步:

class Flask(_PackageBoundObject):
def handle_exception(self, e):
got_request_exception.send(self, exception=e) #信号 - 请求执行出现异常时执行

第5步: 执行了RequestContext 的 pop 方法

class RequestContext(object):
def auto_pop(self, exc):
else:
self.pop(exc)
class RequestContext(object):
def pop(self, exc=_sentinel):
     try:
          if not self._implicit_app_ctx_stack:
         #5.1
              self.app.do_teardown_request(exc)
finally:
       # 请求结束时 request上下文的栈中就把请求pop掉
rv = _request_ctx_stack.pop()
           if app_ctx is not None:
          #5.2
              app_ctx.pop(exc)

第5.1步: 执行  app.do_teardown_request方法

class Flask(_PackageBoundObject):
def do_teardown_request(self, exc=_sentinel):
     # 信号 - 请求执行完毕后自动执行(无论成功与否)
request_tearing_down.send(self, exc=exc)

第5.2步:

class AppContext(object):
def pop(self, exc=_sentinel):
        try:
            if self._refcnt <= 0:
          #5.2.1
                self.app.do_teardown_appcontext(exc)
     # 信号 - 请求上下文pop时执行
appcontext_popped.send(self.app)

第5.2.1步:

class Flask(_PackageBoundObject):
def do_teardown_appcontext(self, exc=_sentinel):
# 信号 - 请求上下文执行完毕后自动执行(无论成功与否)
appcontext_tearing_down.send(self, exc=exc)
 

python-flask-请求源码流程的更多相关文章

  1. Flask信号源码流程

    1. appcontext_pushed = _signals.signal('appcontext-pushed'# 请求app上下文push时执行 return RequestContext(se ...

  2. Flask 请求源码分析

    执行app.run()方法: def run(self, host=None, port=None, debug=None, **options): from werkzeug.serving imp ...

  3. Flask 源码流程,上下文管理

    源码流程 创建对象 from flask import Flask """ 1 实例化对象 app """ app = Flask(__na ...

  4. Flask源码流程分析(一)

    Flask源码流程分析: 1.项目启动: 1.实例化Flask对象 1. 重要的加载项: * url_rule_class = Rule * url_map_class = Map * session ...

  5. Tomcat处理HTTP请求源码分析(下)

    转载:http://www.infoq.com/cn/articles/zh-tomcat-http-request-2 很多开源应用服务器都是集成tomcat作为web container的,而且对 ...

  6. ES6.3.2 index操作源码流程

    ES 6.3.2 index 操作源码流程 client 发送请求 TransportBulkAction#doExecute(Task,BulkRequest,listener) 解析请求,是否要自 ...

  7. Eureka服务端源码流程梳理

    一.简述 spring cloud三步走,一导包,二依赖,三配置为我们简化了太多东西,以至于很多东西知其然不知其所以然,了解底层实现之后对于一些问题我们也可以快速的定位问题所在. spring clo ...

  8. Django session 源码流程

    流程 Django session源码流程 首先执行的是SessionMiddleware的init方法 import_module(settings.SESSION_ENGINE) 导入了一个 dj ...

  9. rest_framework解析器组件源码流程

    rest_framework解析器组件源码流程 解析器顾名思义就是对请求体进行解析.为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数 ...

  10. Django Rest Framework框架源码流程

    在详细说django-rest-framework源码流程之前,先要知道什么是RESTFUL.REST API . RESTFUL是所有Web应用都应该遵守的架构设计指导原则. REST是Repres ...

随机推荐

  1. Docker:Containers

    Prerequisites Install Docker version 1.13 or higher. Read the orientation in Part 1. Give your envir ...

  2. pitch, yaw, roll

    在航空中,pitch, yaw, roll下图所示. pitch是围绕X轴旋转,也叫做俯仰角. yaw是围绕Y轴旋转,也叫偏航角. roll是围绕Z轴旋转,也叫翻滚角.

  3. Vs Code搭建 TypeScript 开发环境

    一.npm install -g typescript 全局安装TypeScript   二.使用Vs Code打开已创建的文件夹,使用快捷键Ctrl+~启动终端输入命令 tsc --init 创建t ...

  4. [0403]学习一个——苟(简单Java开发)

    学习一个--苟 1. 开发目的 拜读了某神犇的blog,感到了自身深深的不足.蒟蒻如我,决定提高一蛤自身的姿势水平,学习一个,使用Java重写用GreatestLanguage写的某小说网站的抓取器. ...

  5. 小程序之map地图上不能在覆盖层

    问题:页面上有一个地图功能,地图上面有两个按钮,是需要覆盖在地图上的,在小程序编辑器中显示是没问题的,但是扫码测试后发现在手机上不显示这两个按钮 解决方法:使用cover-viwe标签包裹一下就可以了

  6. Jmeter干货 不常用却极其有用的几个地方

    1. Jmeter测试计划下Run Thread Groups consecutively 表示序列化执行测试计划下所有线程组中的各个请求 如下图配置,新建的测试计划中,不默认勾选此项, 而享用Jme ...

  7. php 中 public private protected的区别

    public 子类,外部都可调用. protected 子类可以调用,外部不可以调用. private 子类不可以调用,外部不可以调用. <?php class AA { public func ...

  8. linux JAVA_HOME和 java -version不匹配

    ~/.bashrc 中更新了jdk, JAVA_HOME 起效果了,但是java -version还是老的. 原因是/usr/bin/java   和usr/bin/javac是一个链接,得改. 使用 ...

  9. 串口.Qt532测试(同步)

    环境:Win7x64.Qt5.3.2 MSVC OpenGL(x86).vs2010(x86) ZC:这里的例子是 同步的函数操作,貌似 如果子线程在等待 WaitCommEvent(...)或Rea ...

  10. 滑动验证 和滑动图片验证JS

    滑动验证 先放效果图 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...