Flask 请求源码分析

执行app.run()方法:

 def run(self, host=None, port=None, debug=None, **options):
        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:
            #执行,self=app---->执行Flask类的__call__方法
            run_simple(host, port, self, **options)
        finally:
            self._got_first_request = False

run方法

执行Flask.__call__方法:

 #包含请求相关的所有信息
    def __call__(self, environ, start_response):
        """Shortcut for :attr:`wsgi_app`."""
        return self.wsgi_app(environ, start_response)

执行wsgi_app()方法:

 def wsgi_app(self, environ, start_response):
        #获取请求数据,并进行封装和加工,
        ctx = self.request_context(environ)
        #将RequestContext(request,session)封装在Local中
        ‘‘‘
        {‘唯一标识‘:{‘stack‘:[RequestContext(request,session),]}
        }
        ‘‘‘
        ctx.push()
        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)

执行RequestContext.__init__()和push()方法:

__init__():获取request

 def __init__(self, app, environ, request=None):
        self.app = app
        if request is None:
            #实际是一个Request对象,将request信息封装到Request(environ)中,并赋值给RequestContext对象中
            request = app.request_class(environ)
        self.request = request
        self.url_adapter = app.create_url_adapter(self.request)
        self.flashes = None
        self.session = None

push()方法:将RequestContext对象添加到Local中,获取/创建session

   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
        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()
        #将当前requestContext对象放到Local中,
        _request_ctx_stack.push(self)

        # Open the session at the moment that the request context is
        # available. This allows a custom open_session method to use the
        # request context (e.g. code that access database information
        # stored on `g` instead of the appcontext).

        #赋值操作将self.session=SecureCookieSession()        #SecureCookieSession()实际上是一个字典
        self.session = self.app.open_session(self.request)
        if self.session is None:            #用户第一次进来session={}执行,返回NullSession
            self.session = self.app.make_null_session()
当请求进来时:执行open_session()方法:
class SecureCookieSessionInterface(SessionInterface):
    def open_session(self, app, request):
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        #去cookie中获取session作为key,所对应的值(包含了当前用户所有的session数据)
        val = request.cookies.get(app.session_cookie_name)
        #没有
        if not val:
            #返回SecureCookieSession
            return self.session_class()
        max_age = total_seconds(app.permanent_session_lifetime)
        try:
            #val存在的话
            #解密 将加密的字符串解密程字典
            data = s.loads(val, max_age=max_age)
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

这是我们已经将RequestContext(request,session)的对象放到Local中,并且request和session的初始值也获取到了,

上面方法就是为了执行下面两句:

 ctx = self.request_context(environ) ctx.push()

执行上面的语句后我们在wagi_app()方法中继续向下执行,触发视图函数Flask类中
    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
        """
        #执行@before_first_request所装饰的所有函数
        self.try_trigger_before_first_request_functions()
        try:
            request_started.send(self)
            #执行@before_request装饰的所有函数 看是否有返回值
            rv = self.preprocess_request()
            if rv is None:
                rv = self.dispatch_request()
        except Exception as e:
            rv = self.handle_user_exception(e)
        #执行@after_request所装饰的所有函数
        return self.finalize_request(rv)

执行finalize_request()方法:

    def finalize_request(self, rv, from_error_handler=False):
        """Given the return value from a view function this finalizes
        the request by converting it into a response and invoking the
        postprocessing functions.  This is invoked for both normal
        request dispatching as well as error handlers.

        Because this means that it might be called as a result of a
        failure a special safe mode is available which can be enabled
        with the `from_error_handler` flag.  If enabled, failures in
        response processing will be logged and otherwise ignored.

        :internal:
        """
        response = self.make_response(rv)
        try:
            #执行@after_request所装饰的所有函数
            response = self.process_response(response)
            request_finished.send(self, response=response)
        except Exception:
            if not from_error_handler:
                raise
            self.logger.exception(‘Request finalizing failed with an ‘
                                  ‘error while handling an error‘)
        return response

执行process_response()方法

    def 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)
        #保存session
        if not self.session_interface.is_null_session(ctx.session):
            self.save_session(ctx.session, response)
        return response

具体是怎么保存session的呢?

看save_session()方法

session.py中

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)

        # Delete case.  If there is no session we bail early.
        # If the session was modified to be empty we remove the
        # whole cookie.
        if not session:
            if session.modified:
                response.delete_cookie(app.session_cookie_name,
                                       domain=domain, path=path)
            return

        # Modification case.  There are upsides and downsides to
        # emitting a set-cookie header each request.  The behavior
        # is controlled by the :meth:`should_set_cookie` method
        # which performs a quick check to figure out if the cookie
        # should be set or not.  This is controlled by the
        # SESSION_REFRESH_EACH_REQUEST config flag as well as
        # the permanent flag on the session itself.
        if not self.should_set_cookie(app, session):
            return

        httponly = self.get_cookie_httponly(app)
        secure = self.get_cookie_secure(app)
        expires = self.get_expiration_time(app, session)        #加密操作
        val = self.get_signing_serializer(app).dumps(dict(session))        #将session保存在cookie中
        response.set_cookie(app.session_cookie_name, val,
                            expires=expires, httponly=httponly,
                            domain=domain, path=path, secure=secure)

globals.py

def _lookup_req_object(name):
    # top实际是一个RequestContext对象
    ‘‘‘
            {
            ‘唯一标识‘:{‘stack‘:[RequestContext(request,session),]}
            }
            ‘‘‘
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError(_request_ctx_err_msg)

    #得到session:RequestContext(request,session).session
    #得到request:RequestContext(request,session).request
    return getattr(top, name)

def _lookup_app_object(name):
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return getattr(top, name)

def _find_app():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError(_app_ctx_err_msg)
    return top.app

# context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, ‘request‘))
session = LocalProxy(partial(_lookup_req_object, ‘session‘))
g = LocalProxy(partial(_lookup_app_object, ‘g‘))

原文地址:https://www.cnblogs.com/ctztake/p/8245057.html

时间: 2024-09-30 09:56:21

Flask 请求源码分析的相关文章

Flask上下文源码分析(一)

flask中的上下文分两种,application context和request context,即应用上下文和请求上下文. 从名字上看,可能会有误解,认为应用上下文是一个应用的全局变量,所有请求都可以访问修改其中的内容:而请求上下文则是请求内可访问的内容. 但事实上,这两者并不是全局与局部的关系,它们都处于一个请求的局部中. 先说结论:每个请求的g都是独立的,并且在整个请求内都是可访问修改的. 下面来研究一下. 上下文类的定义: 上下文类定义在flask.ctx模块中 class AppCo

Flask框架 —— session源码分析

目录 session源码分析 1.请求来了,执行__call__方法 2.__call__方法 3.调用__call__方法 3.1.ctx = self.request_context(environ) --- 得到空的session 3.2.ctx.push() --- 调用open_session方法 3.3.self.full_dispatch_request() --- 路由分发,执行函数,写入session session源码分析 1.请求来了,执行__call__方法 # 请求来了

爬虫5 scrapy框架2 全站爬取cnblogs, scarpy请求传参, 提高爬取效率, 下载中间件, 集成selenium, fake-useragent, 去重源码分析, 布隆过滤器, 分布式爬虫, java等语言概念补充, bilibili爬视频参考

1 全站爬取cnblogs # 1 scrapy startproject cnblogs_crawl # 2 scrapy genspider cnblogs www.cnblogs.com 示例: # cnblogs_crawl/cnblogs_crawl/spiders/cnblogs.py import scrapy from cnblogs_crawl.items import CnblogsCrawlItem from scrapy.http import Request class

jQuery源码分析系列(33) : AJAX中的前置过滤器和请求分发器

jQuery1.5以后,AJAX模块提供了三个新的方法用于管理.扩展AJAX请求,分别是: 1.前置过滤器 jQuery. ajaxPrefilter 2.请求分发器 jQuery. ajaxTransport, 3.类型转换器 ajaxConvert 源码结构: jQuery.extend({ /** * 前置过滤器 * @type {[type]} */ ajaxPrefilter: addToPrefiltersOrTransports(prefilters), /** * 请求分发器 *

详解SpringMVC请求的时候是如何找到正确的Controller[附带源码分析]

目录 前言 源码分析 重要接口介绍 SpringMVC初始化的时候做了什么 HandlerExecutionChain的获取 实例 资源文件映射 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 我们使用浏览器通过地址 http://ip:port/contextPath/path进行访问

Tomcat7.0源码分析——请求原理分析(中)

前言 在<TOMCAT7.0源码分析--请求原理分析(上)>一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握<TOMCAT7.0源码分析--请求原理分析(上)>一文中的相关知识以及HTTP协议和TCP协议的一些内容.本文重点讲解Tomcat7.0在准备好接受请求后,请求过程的原理分析. 请求处理架构 在正式开始之前,我们先来看看图1中的Tomcat请求处理架构. 图1 Tomcat请求处理架构 图1列出了Tomcat请求处理架构中的主

Tomcat源码分析——请求原理分析(下)

前言 本文继续讲解TOMCAT的请求原理分析,建议朋友们阅读本文时首先阅读过<TOMCAT源码分析——请求原理分析(上)>和<TOMCAT源码分析——请求原理分析(中)>.在<TOMCAT源码分析——请求原理分析(中)>一文我简单讲到了Pipeline,但并未完全展开,本文将从Pipeline开始讲解请求原理的剩余内容. 管道 在Tomcat中管道Pipeline是一个接口,定义了使得一组阀门Valve按照顺序执行的规范,Pipeline中定义的接口如下: getBas

Tomcat7.0源码分析——请求原理分析(上)

前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多人早期的J2EE项目,由程序员自己实现Jsp页面或者Servlet接受请求,后来借助Struts1.Struts2.Spring等中间件后,实际也是利用Filter或者Servlet处理请求,大家肯定要问了,这些Servlet处理的请求来自哪里?Tomcat作为Web服务器是怎样将HTTP请求交给Servlet的呢? 本文就

zookeeper源码分析之五服务端(集群leader)处理请求流程

leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcessor -> ProposalRequestProcessor ->CommitProcessor -> Leader.ToBeAppliedRequestProcessor ->FinalRequestProcessor 具体情况可以参看代码: @Override protected v