python-flask-请求源码流程

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

class Flask(_PackageBoundObject):
   def
run(self, host=None, port=None, debug=None, **options):from werkzeug.serving import run_simpletry:#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]raisereturn 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 = appif request is None:request = app.request_class(environ)
       #app.request_class = Request self.request
= requestself.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.topif 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_ctxAppContext对象,就要先去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_identexcept ImportError:from _thread import get_identclass 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_keys = 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) ##返回了一个 类似字典对象,对象里面有dataexcept 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#获取请求的urlrule = 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.1response
= 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_functionsfor 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_bytesclass MySession(dict, SessionMixin):def __init__(self, initial=None, sid=None):self.sid = sidself.initial = initialsuper(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 = MySessioncontainer = {# '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 Nonereturn 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步: 执行了RequestContextpop 方法

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)

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/liuwei0824/p/8304097.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/368166.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

正则表达式强化,爬虫练习

re模块下的常用方法 import re ret re.findall(\d(\.\d)?, 1.232.34)   print(ret) 结果&#xff1a; [.23,.34] # findall的正则表达式里面有分组(),()里面的内容优先显示 ret re.findall(\d(?:\.\d)?, 1.232.34)print(ret)    结果&#xff1a;…

Linux LVM管理

LVM(Logical Volume Manager)逻辑卷管理是在Linux2.4内核以上实现的磁盘管理技术。它是Linux环境下对磁盘分区进行管理的一种机制。 本文内容&#xff1a; 创建和管理LVM扩容LVM分区一、创建和管理LVM 要创建一个LVM系统&#xff0c;一般需要经过以下步骤&#xff1a; 1、 创建…

USB OTG插入检测识别

一 USB引脚一般四根线&#xff0c;定义如下&#xff1a; 为支持OTG功能&#xff0c;mini/micro usb接口扩展了一个ID引脚&#xff08;第4脚&#xff09; A设备端ID脚接地&#xff0c;则初始状态为Host&#xff0c;例如PC和支持OTG设备做主设备时 B设备端ID脚悬空&#xff0c;默…

CSS3与页面布局学习笔记(三)——BFC、定位、浮动、7种垂直居中方法

一、BFC与IFC 1.1、BFC与IFC概要 BFC&#xff08;Block Formatting Context&#xff09;即“块级格式化上下文”&#xff0c; IFC&#xff08;Inline Formatting Context&#xff09;即行内格式化上下文。常规流&#xff08;也称标准流、普通流&#xff09;是一个文档在被显示…

Java垃圾回收(2)

并行清理 今天&#xff0c;我们介绍了并行GC的工作原理。 具体来说&#xff0c;这是在Eden上运行Parallel Scavenge收集器&#xff0c;在Tenured一代中运行Parallel Mark and Sweep收集器的组合。 您可以通过传递-XX&#xff1a; UseParallelOldGC来获得此选项&#xff0c;尽管…

Navicat Premium创建MySQL存储过程

1、使用Navicat Premium打开创建函数向导&#xff0c;操作&#xff1a;连接名——数据库——函数——新建函数 2、选择过程——输入存储过程参数——完成&#xff08;这一步可以不填写参数&#xff0c;编写存储过程代码的时候设置参数&#xff09; 3、按照要求完成存储过程代码…

CSS3与页面布局学习笔记(二)——盒子模型(Box Model)、边距折叠、内联与块标签、CSSReset

一、盒子模型&#xff08;Box Model&#xff09; 盒子模型也有人称为框模型&#xff0c;HTML中的多数元素都会在浏览器中生成一个矩形的区域&#xff0c;每个区域包含四个组成部分&#xff0c;从外向内依次是&#xff1a;外边距&#xff08;Margin&#xff09;、边框&#xff…

mysql中将某个字段做计算,mysql创建计算字段使用子查询教程

作为计算字段使用子查询使用子查询的另一方法是创建计算字段。假如需要显示 customers表中每个客户的订单总数。订单与相应的客户ID存储在 orders 表中。为了执行这个操作&#xff0c;遵循下面的步骤。(1) 从 customers 表中检索客户列表。(2) 对于检索出的每个客户&#xff0c…

Android GreenDao使用教程

一、Greendao简介 Greendao是一款用于数据库创建与管理的框架&#xff0c;由于原生SQLite语言比较复杂繁琐&#xff0c;使得不少程序员不得不去学习SQLite原生语言&#xff0c;但是学习成本高&#xff0c;效率低下&#xff0c;所以不少公司致力于开发一款简单的数据库管理框架&…

Java垃圾回收(1)

这是有关垃圾收集&#xff08;GC&#xff09;的系列文章中的第一篇。 我希望能够涵盖整个系列过程中的理论知识以及热点虚拟机中的所有主要收集器。 这篇文章仅说明什么是垃圾回收&#xff0c;以及不同回收器共有的元素。 我为什么要在乎&#xff1f; 您的Java虚拟机可以为您管…

少锁定Java对象池

自从我写任何东西以来已经有一段时间了&#xff0c;我一直在忙于我的新工作&#xff0c;该工作涉及在性能调整方面做一些有趣的工作。 挑战之一是减少应用程序关键部分的对象创建。 尽管Java随着时间的推移改进了GC算法&#xff0c;但垃圾回收打h一直是Java的主要难题。 Azul是…

php数据库postgresql,PHP 操作 PostgreSQL数据库

1.要让PHP支持PostgreSQL&#xff0c;就需要重新编译PHP&#xff1b;./configure --prefix/usr/local/php5 --with-apxs2/usr/local/apache2/bin/apxs --with-mysql/usr/local/mysql --with-config-file-path/usr/local/php5 --with-zlib --enable-mbstringall --with-mysql…

uestc summer training #2

A 增广 #include<bits/stdc.h> using namespace std; const int MAXN 1000000 10; vector<int> g[MAXN]; int a[MAXN], b[MAXN], sz[MAXN], cnt[MAXN]; bool mg[MAXN], vis[MAXN]; int n, m; bool dfs(int u, int f -1) {if (g[u].empty()) //如果当前数没有位…

mysql有实例名这个概念,MySQL的一些概念笔记

1.MySQL Server、MySQL实例、MySQL数据库MySQL数据库指的是实际存在的物理操作系统文件的集合&#xff0c;也可以指逻辑数据的集合。为了访问、处理数据&#xff0c;我们需要一个数据库管理系统&#xff0c;也就是MySQL Server(也称为MySQL服务器)。MySQL实例指的是MySQL进程及…

python基础学习笔记(十三)

re模块包含对 正则表达式。本章会对re模块主要特征和正则表达式进行介绍。 什么是正则表达式 正则表达式是可以匹配文本片段的模式。最简单的正则表达式就是普通字符串&#xff0c;可以匹配其自身。换包话说&#xff0c;正则表达式’python’ 可以匹配字符串’python’ 。你可以…

OD debug matlab,OllyDebug基本使用方法

OD是逆向过程中最好的动态调试工具&#xff0c;这次来记录学习笔记。(特别鸣谢石总)1、OD的工作界面最开始要学的就是界面的使用N了吧&#xff0c;这里分各个框来解释下&#xff1a;列举各个框用处&#xff1a;可以看到下面一框框东西&#xff1a;这些一时半会用不到&#xff0…

设计模式:策略

这次我想谈谈策略设计模式 。 通过这种方式&#xff0c;我开始撰写有关行为设计模式的文章。 这些模式表示对象之间的某些交互模式&#xff0c;以使代码更灵活且组织得更好。此方法的最本质点是对象之间的松散耦合。 当您的应用程序中有多个实现目的的实现时&#xff0c;应使用…

本地搭建WordPress (XAMPP环境)

1&#xff0c;XAMPP是一个流行的PHP开发环境&#xff0c;官网下载&#xff1a; https://www.apachefriends.org/zh_cn/index.html 然后安装。 官方介绍&#xff1a;XAMPP是最流行的PHP开发环境 XAMPP是完全免费且易于安装的Apache发行版&#xff0c;其中包含MariaDB、PHP和Pe…

CSS3 选择器——属性选择器

上一节在《CSS3选择器——基本选择器》中主要介绍了CSS3选择器的第一部分&#xff0c;这节主要和大家一起来学习CSS3选择器的第二部分——属性选择器。属性选择器早在CSS2中就被引入了&#xff0c;其主要作用就是对带有指定属性的HTML 元素设置样式。使用CSS3属性选择器&#x…

设计模式:生成器

有时需要在应用程序中创建一个复杂的对象。 一种解决方案是Factory模式&#xff0c;另一种是Builder设计模式。 在某些情况下&#xff0c;您甚至可以结合使用这两种模式。 但是在本文中&#xff0c;我想研究一下Builder设计模式 。 我需要说的第一件事是创造模式。 在什么情况…