Flask学习-Flask请求处理过程

一、__call__()

在app启动后,一旦uwsgi收到来自web server的请求,就会调用app,其实此时就是调用app的__call__(environ,start_response).

flask.py:

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

  

二、wsgi_app()

当http请求从server发送过来的时候,他会启动__call__功能,这时候就启动了最关键的wsgi_app(environ,start_response)函数。

fask.py:

class Flask(_PackageBoundObject):  

........
    #请注意函数的说明,说得非常准确,这个wsgi_app是一个真正的WSGI应用
    def wsgi_app(self, environ, start_response):    #他扮演的是一个中间角色
        """The actual WSGI application.  This is not implemented in
        `__call__` so that middlewares can be applied without losing a
        reference to the class.  So instead of doing this:: 

            app = MyMiddleware(app) 

        It‘s a better idea to do this instead:: 

            app.wsgi_app = MyMiddleware(app.wsgi_app) 

        Then you still have the original application object around and
        can continue to call methods on it. 

        :param environ: a WSGI environment
        :param start_response: a callable accepting a status code,
                               a list of headers and an optional
                               exception context to start the response
        """
        ctx = self.request_context(environ)  #
        ctx.push()
        error = None
        try:
            try:
                response = self.full_dispatch_request()    #full_dispatch_request起到了预处理和错误处理以及分发请求的作用
            except Exception as e:
                error = e
                response = self.make_response(self.handle_exception(e))  #如果有错误发生,则生成错误响应
            return response(environ, start_response)       #如果没有错误发生,则正常响应请求,返回响应内容
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

  

三、WSGI_APP的内部流程

第一步:生成request请求对象和请求上下文环境

首先,你会看到ctx = self.request_context(environ)的语句,这个涉及到Flask使用了请求上下文和应用上下文的概念,结构为栈结构,这部分比较难,后面第二篇会单独写。

这里只需要理解为,上面语句产生的所用是生成了一个request请求对象以及包含请求信息在内的request context

第二部:请求进入预处理,错误处理及请求转发到响应的过程

进入wsgi_app的函数内部,生成了request对象和上下文环境之后,进入到try

[python] view plain copy

  1. response = self.full_dispatch_request()

我们看到,响应被赋值成了full_dispatch_request()方法的返回内容,所以我们来看一下full_dispatch_request方法

[python] view plain copy

  1. class Flask(_PackageBoundObject):
  2. #此处省略一些代码
  3. def full_dispatch_request(self):
  4. """Dispatches the request and on top of that performs request
  5. pre and postprocessing as well as HTTP exception catching and
  6. error handling.
  7. .. versionadded:: 0.7
  8. """
  9. self.try_trigger_before_first_request_functions()  #进行发生真实请求前的处理
  10. try:
  11. request_started.send(self)                     #socket部分的操作
  12. rv = self.preprocess_request()                 #进行请求的预处理
  13. if rv is None:
  14. rv = self.dispatch_request()
  15. except Exception as e:
  16. rv = self.handle_user_exception(e)
  17. response = self.make_response(rv)
  18. response = self.process_response(response)
  19. request_finished.send(self, response=response)
  20. return response

他首先会触发 try_trigger_before_first_request_function()方法

在方法内部 ---------->会触发 _got_first_request 属性,这个属性的返回值是True或者False.  True的话就代表了程序开始处理请求了.

来看看 try_trigger_before_first_request_function()的代码,他的目的是,最后将_got_first_request属性置为True.

[python] view plain copy

  1. class Flask(_PackageBoundObject):
  2. #省略一些代码
  3. def try_trigger_before_first_request_functions(self):
  4. """Called before each request and will ensure that it triggers
  5. the :attr:`before_first_request_funcs` and only exactly once per
  6. application instance (which means process usually).
  7. :internal:
  8. """
  9. if self._got_first_request:
  10. return
  11. with self._before_request_lock:
  12. if self._got_first_request:
  13. return
  14. for func in self.before_first_request_funcs:
  15. func()
  16. self._got_first_request = True

再来看看_got_first_request 的定义,他的默认值是False

他的定义中可以明显看到, if the application started,this attribute is set to True.

[python] view plain copy

  1. class Flask(_PackageBoundObject):
  2. #省略一些代码
  3. @property
  4. def got_first_request(self):
  5. """This attribute is set to ``True`` if the application started
  6. handling the first request.
  7. .. versionadded:: 0.8
  8. """
  9. return self._got_first_request

接着,当_got_first_request 属性被设置完以后,我们就需要再次回到 full_dispatch_request函数内部,继续往下走

下面一段代码是request_started.send(),他是继承自signal模块,大致作用是进行socket部分的功能,暂时不详细追溯。

preprocess_request()方法的话,主要是进行flask的hook钩子, before_request功能的实现,也就是在真正发生请求之前,有些事情需要提前做

Flask一共有4个hook钩子,另外再写吧

随后,继续往下走,来到了一个至  关  重  要的功能  dispatch_request()

[python] view plain copy

  1. try:
  2. request_started.send(self)
  3. rv = self.preprocess_request()
  4. if rv is None:
  5. rv = self.dispatch_request()

为什么说至关重要,因为一个http请求到了这里,实际上已经完成了从wsgi部分的过渡,进入到了寻找响应的阶段了,一个请求通过url进来以后,app怎么知道要如何响应呢?

就是通过dispatch_request方法来进行请求判定和分发。

第三步:请求分发  dispatch_request

来看源码

[python] view plain copy

  1. class Flask(_PackageBoundObject):
  2. #省略一些代码
  3. def dispatch_request(self):   #看函数定义,matches the URL and returns the value of the view or error.
  4. """Does the request dispatching.  Matches the URL and returns the
  5. return value of the view or error handler.  This does not have to
  6. be a response object.  In order to convert the return value to a
  7. proper response object, call :func:`make_response`.
  8. .. versionchanged:: 0.7
  9. This no longer does the exception handling, this code was
  10. moved to the new :meth:`full_dispatch_request`.
  11. """
  12. req = _request_ctx_stack.top.request
  13. if req.routing_exception is not None:
  14. self.raise_routing_exception(req)
  15. rule = req.url_rule
  16. # if we provide automatic options for this URL and the
  17. # request came with the OPTIONS method, reply automatically
  18. if getattr(rule, ‘provide_automatic_options‘, False) \
  19. and req.method == ‘OPTIONS‘:
  20. return self.make_default_options_response()
  21. # otherwise dispatch to the handler for that endpoint
  22. return self.view_functions[rule.endpoint](**req.view_args)   #最终进入view_functions,取出url对应的视图函数的返回值

中间不需要过多考虑,req = _request_ctx_stack.top.request 可以暂时理解为,将请求对象赋值给req

这里先简单讲下,每个url进来以后,他都会对应一个view_function

比如下面的一个简单视图函数,路径  ‘/‘ 对应的是index函数

但是,实际上当中是分2步走的,第一步是‘/‘对应的endpoint为‘index‘ ,第二部是endpoint  ‘index‘ 对应到index()视图函数

这个也是放在第二篇文章里面具体写flask 路由的实现,这里暂时可以忽略中间步骤,只要知道URL----------->VIEW FUNCTION的逻辑步骤就ok

[python] view plain copy

  1. @app.route(‘/‘)
  2. def index():
  3. return ‘Hello world‘

另外说下view_functions 是一个字典形式,他的key和value的关系是endpoint ------> view function

所以每个有效的URL进来,都能找到他对应的视图函数view function,取得返回值并赋值给  rv

比如上面简单的index,他取得的就是 ‘Hello world‘ 值

请求分发完成后,已经取得了返回的值,再看下一步是如何做

我们再次回到  full_dispatch_request方法内往下走

[html] view plain copy

  1. response = self.make_response(rv)
  2. response = self.process_response(response)
  3. request_finished.send(self, response=response)
  4. return response

这时候,通过make_response函数,将刚才取得的 rv 生成响应,重新赋值response

再通过process_response功能主要是处理一个after_request的功能,比如你在请求后,要把数据库连接关闭等动作,和上面提到的before_request对应和类似。

之后再进行request_finished.send的处理,也是和socket处理有关,暂时不详细深入。

之后返回新的response对象

这里特别需要注意的是,make_response函数是一个非常重要的函数,他的作用是返回一个response_class的实例对象,也就是可以接受environ和start_reponse两个参数的对象

非   常   重   要!!!

Converts the return value from a view function to a real response object that is an instance of :attr:`response_class

[python] view plain copy

  1. class Flask(_PackageBoundObject):   #注意函数说明,converts the return value from view function to a real response object
  2. #省略一部分代码
  3. def make_response(self, rv):
  4. """Converts the return value from a view function to a real
  5. response object that is an instance of :attr:`response_class`.
  6. The following types are allowed for `rv`:
  7. .. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
  8. ======================= ===========================================
  9. :attr:`response_class`  the object is returned unchanged
  10. :class:`str`            a response object is created with the
  11. string as body
  12. :class:`unicode`        a response object is created with the
  13. string encoded to utf-8 as body
  14. a WSGI function         the function is called as WSGI application
  15. and buffered as response object
  16. :class:`tuple`          A tuple in the form ``(response, status,
  17. headers)`` or ``(response, headers)``
  18. where `response` is any of the
  19. types defined here, `status` is a string
  20. or an integer and `headers` is a list or
  21. a dictionary with header values.
  22. ======================= ===========================================
  23. :param rv: the return value from the view function
  24. .. versionchanged:: 0.9
  25. Previously a tuple was interpreted as the arguments for the
  26. response object.
  27. """
  28. status_or_headers = headers = None
  29. if isinstance(rv, tuple):
  30. rv, status_or_headers, headers = rv + (None,) * (3 - len(rv))
  31. if rv is None:
  32. raise ValueError(‘View function did not return a response‘)
  33. if isinstance(status_or_headers, (dict, list)):
  34. headers, status_or_headers = status_or_headers, None
  35. if not isinstance(rv, self.response_class):
  36. # When we create a response object directly, we let the constructor
  37. # set the headers and status.  We do this because there can be
  38. # some extra logic involved when creating these objects with
  39. # specific values (like default content type selection).
  40. if isinstance(rv, (text_type, bytes, bytearray)):
  41. rv = self.response_class(rv, headers=headers,
  42. status=status_or_headers)
  43. headers = status_or_headers = None
  44. else:
  45. rv = self.response_class.force_type(rv, request.environ)
  46. if status_or_headers is not None:
  47. if isinstance(status_or_headers, string_types):
  48. rv.status = status_or_headers
  49. else:
  50. rv.status_code = status_or_headers
  51. if headers:
  52. rv.headers.extend(headers)
  53. return rv

第四步:返回到wsgi_app内部

终于快进行到了最后一步,流程走回到了wsgi_app的内部

下面这段是wsgi_app内部的代码

[python] view plain copy

  1. try:
  2. try:
  3. response = self.full_dispatch_request()
  4. except Exception as e:
  5. error = e
  6. response = self.make_response(self.handle_exception(e))
  7. return response(environ, start_response)
  8. finally:
  9. if self.should_ignore_error(error):
  10. error = None
  11. ctx.auto_pop(error)

当response从刚刚的full_dispatch_request功能返回之后,函数会对这个response加上environ, start_response的参数并返回给Gunicorn

至此,一个HTTP从请求到响应的流程就完毕了.

总的来说,一个流程的关键步骤可以简单归结如下:

后一篇,将会记录一下flask的route实现,里面的url如何和endpoint对应起来,endpoint和view function又是如何对应起来

原文地址:https://www.cnblogs.com/skyflask/p/9194224.html

时间: 2024-11-08 20:41:31

Flask学习-Flask请求处理过程的相关文章

[Flask]学习Flask第三天笔记总结

1 from flask import Flask,render_template,request 2 from others import checkLogin 3 app = Flask(__name__) 4 5 #从templates里引用index.html 6 #return render_template("index.html") 7 @app.route('/') 8 def index(): 9 return '''<form action="/lo

Flask学习-Wsgiref库

一.前言 前面在Flask学习-Flask基础之WSGI中提到了WerkZeug,我们知道,WerkZeug是一个支持WSGI协议的Server,其实还有很多其他支持WSGI协议的Server.http://wsgi.readthedocs.io/en/latest/servers.html,这里可以看到有uwsgi.werkzeug.serving.wsgiref.python-fastcgi等等几十个.wsgiref是官方给出的一个实现了WSGI标准用于演示用的简单Python内置库,它实现

[Flask]学习杂记--模板

这个学习杂记主要不是分享经验,更多是记录下falsk的体验过程,以后做东西在深入研究,因为django之前用的时间比较长,所以很多概念都是一看而过,做个试验了解下flask的功能. flask中使用是jinja2 模板,和django自带的模板很类似,但又比django的模板强大,提供了更多有用的工具,基本使用上基本都是一致的. 写了一个小案例,把模板渲染的基本步骤和变量传递的基本使用练习了下. jinjia2 模板文档 http://jinja.pocoo.org/docs/dev/ flas

Flask学习之六 个人资料和头像

英文博客地址:http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-vi-profile-page-and-avatars 中文翻译地址:http://www.pythondoc.com/flask-mega-tutorial/profile.html 开源中国社区:http://www.oschina.net/translate/the-flask-mega-tutorial-part-vi-profile-page-

Flask学习-前言

前言 使用Flask断断续续加起来快一年了,但是一直没有从源码层去了解其实现原理.加上自己python基础不扎实,所以准备从看一个开源项目开始,希望能够从中窥得武功精髓,让自己水平更上一层楼. Flask Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架.Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要

flask基础之请求处理核心机制(五)

前言 总结一下flask框架的请求处理流程. 系列文章 flask基础之安装和使用入门(一) flask基础之jijia2模板使用基础(二) flask基础之jijia2模板语言进阶(三) flask基础之app初始化(四) WSGI协议 一般来说http服务器和框架需要进行解耦,http专门负责接受HTTP请求.解析HTTP请求.发送HTTP,响应请求等:而web框架负责处理请求的逻辑,和数据库的交互等等,那么它们之间需要约定一套接口使得http服务器能够调用web框架的处理逻辑,这个协议就是

Flask 学习(四)静态文件

Flask 学习(四)静态文件 动态 web 应用也需要静态文件,一般是 CSS 和 JavaScript 文件.理想情况下你的服务器已经配置好提供静态文件的服务. 在开发过程中, Flask 也能做好这个工作. 静态文件引用 我们先来看下普通的 html 引用静态文件,如 css(js也同样,就不多加示例了),以下为一简单实例,直接打开html: flask 处理 —— static 若直接将该html 当成 flask 模板,相对路径自然就失效了,静态文件将不会被成功读取. 那在flask中

python web服务学习——flask

flask是一个使用 Python 编写的轻量级 Web 应用框架.也是开发python web服务最常用的框架之一. 第一步当然是安装flask,依然是pip安装: 学习flask可以从官网的quick start(http://flask.pocoo.org/docs/0.10/quickstart/#hooking-in-wsgi-middlewares)开始,这里有许多的小例子,先看一个最简单的 from flask import Flask app = Flask(__name__)

flask 学习

最近学习flask框架,是照着图灵丛书<Flask Web开发>来学的. 照着第2章写了个简单的框架 #_*_coding:utf-8 _*_ from flask import Flask app = Flask(__name__) @app.route('/') def index(): return '<h1>hello</h1>' @app.route('/hello') def hello(): return '<h2>hello,there<