WSGI in Flask --2

124 篇文章 1 订阅

上篇笔记有了wsgi_app的概念。
wsgi_app用来连接WSGI server和应用程序。
主要功能是调用各种函数来处理请求,然后将处理结果返回给服务器。
这篇笔记来看wsgi_app 中具体处理请求(目前就是访问一个网址)的过程。

Flask 从客户端收到请求时,要让视图函数能访问一些对象,才能处理请求。例如视图函数需要知道它执行情况的请求信息(请求的 url,参数,方法等)以及应用信息(应用中初始化的数据库等),才能够正确运行。
Flask 中使用上下文把这些信息变成类似全局变量的东西。


wsgi_app()函数内部第一句ctx = self.request_context(environ)开始看:

  1. ctx for context,这里就是上下文的意思。flask中上下文分为应用上下文和请求上下文。
  2. 使用request_context() 方法(RequestContext类)来实例化一个ctx对象。可以看出,ctx使用栈(stack)结构来存储信息。
  3. environWSGI server 提供。
  4. 实例化ctx 后,wsgi_app()中接着使用了ctx.push(),把上下文内容进行压栈,放在了栈顶。
  5. 上面语句产生的所用是生成了一个request请求对象以及包含请求信息在内的request context。
    def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        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)

def request_context(self, environ):
    return RequestContext(self, environ)

RequestContext()中看完整的push():

  1. _request_ctx_stack = LocalStack()_request_ctx_stack 为实例化的LocalStack()类。
  2. top = _request_ctx_stack.top 即将栈顶的元素赋给top,ctx.push()语句没有传入数据,这里top = None
  3. 同样的 app_ctx = None, 不会执行if 语句之后的内容。
  4. 关键的_request_ctx_stack.push(self)语句,这里self为上下文实例 ctx 本身,将上下文内容压缩到栈中,放在栈顶。
class RequestContext(object):
    """The request context contains all request relevant information.  It is
    created at the beginning of the request and pushed to the
    `_request_ctx_stack` and removed at the end of it.  It will create the
    URL adapter and request object for the WSGI environment provided.
    """
    ...
    def push(self):
        """Binds the request context to the current context."""
        top = _request_ctx_stack.top
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)

        # Before we push the request context we have to ensure that there is an application context.
        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()

        _request_ctx_stack.push(self)

        self.session = self.app.open_session(self.request)
        if self.session is None:
            self.session = self.app.make_null_session()
# 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'))



有了上下文的概念,继续看 wsgi_app():

response = self.full_dispatch_request(),将full_dispatch_request()方法的返回内容赋值给response,最后返回到服务器。

full_dispatch_request() 主要功能就是分发请求:

    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
        """
        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)

__init__函数中设置了self._got_first_request = Falsetry_trigger_before_first_request_functions()的最后一句设置了self._got_first_request = True,即got_first_request(self) 中描述的 This attribute is set to True if the application started handling the first request.
表明现在开始处理程序了。

    def try_trigger_before_first_request_functions(self):
        """Called before each request and will ensure that it triggers
        the :attr:`before_first_request_funcs` and only exactly once per
        application instance (which means process usually).

        :internal:
        """
        if self._got_first_request:
            return
        with self._before_request_lock:
            if self._got_first_request:
                return
            for func in self.before_first_request_funcs:
                func()
            self._got_first_request = True
    def got_first_request(self):
        """This attribute is set to ``True`` if the application started
        handling the first request.

        .. versionadded:: 0.8
        """
        return self._got_first_request



接着看 full_dispatch_request()try 中的 request_started.send(self)
根据介绍, request_started 是一个信号名,表明开始处理请求,最后返回信号名和信号内容。 send(self) 方法将请求返回信号本身。

request_started = _signals.signal('request-started')
_signals = Namespace()
    class Namespace(object):
        def signal(self, name, doc=None):
            return _FakeSignal(name, doc)
    class _FakeSignal(object):
        """If blinker is unavailable, create a fake class with the same
        interface that allows sending of signals but will fail with an
        error on anything else.  Instead of doing anything on send, it
        will just ignore the arguments and do nothing instead.
        """

        def __init__(self, name, doc=None):
            self.name = name
            self.__doc__ = doc
        def _fail(self, *args, **kwargs):
            raise RuntimeError('signalling support is unavailable '
                               'because the blinker library is '
                               'not installed.')
        send = lambda *a, **kw: None
        connect = disconnect = has_receivers_for = receivers_for = \
            temporarily_connected_to = connected_to = _fail
        del _fail

full_dispatch_request 接下来运行 rv = self.preprocess_request() ,对请求进行预处理。
__init__ 中设置了self.url_value_preprocessors = {}self.before_request_funcs = {} ,这里啥也没运行。只有在运行url_value_preprocessorbefore_request() 才会运行之后的代码。

    def preprocess_request(self):
        """Called before the actual request dispatching and will
        call each :meth:`before_request` decorated function, passing no
        arguments.
        If any of these functions returns a value, it's handled as
        if it was the return value from the view and further
        request handling is stopped.

        This also triggers the :meth:`url_value_preprocessor` functions before
        the actual :meth:`before_request` functions are called.
        """
        bp = _request_ctx_stack.top.request.blueprint

        funcs = self.url_value_preprocessors.get(None, ())
        if bp is not None and bp in self.url_value_preprocessors:
            funcs = chain(funcs, self.url_value_preprocessors[bp])
        for func in funcs:
            func(request.endpoint, request.view_args)

        funcs = self.before_request_funcs.get(None, ())
        if bp is not None and bp in self.before_request_funcs:
            funcs = chain(funcs, self.before_request_funcs[bp])
        for func in funcs:
            rv = func()
            if rv is not None:
                return rv



预处理之后 rv=None , 开始运行 rv = self.dispatch_request():

  1. _request_ctx_stack,即请求上下文对象,在运行wsgi_app() 时将请求上下文放在了栈顶。
  2. _request_ctx_stack.top.request保存着当前请求信息。在每次请求过来的时候,flask 会把当前请求的信息保存进去,这样我们就能在整个请求处理过程中使用它。
  3. 主要看函数最后返回值,返回了view_functions ,在__init__ 中设置了self.view_functions = {} ,空字典。只有运行add_url_rule()时才会将URL和视图函数的映射关系添加到字典中。
  4. dispatch_request()处理结果是得到了一个 URL endpoint (例如‘/’,’/index’)和视图函数的映射关系字典,full_dispatch_request() 中将处理结果赋值给 rv
    def dispatch_request(self):
        """Does the request dispatching.  Matches the URL and returns the return value of the view or error handler. 
        """
        req = _request_ctx_stack.top.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)



再回到 full_dispatch_request(),函数最后返回了 self.finalize_request(rv),即使用 finalize_request方法来处理 rv

  1. response = self.make_response(rv) ,make_response(rv)介绍: Converts the return value from a view function to a real response object that is an instance of : attr: response_class. 将视图函数中的内容变成真正的响应。
  2. process_response(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. 请求正常处理之后的 ,结果返回到WSGI server 之前再处理一遍 response
  3. finalize_request()最后返回response
  4. full_dispatch_request 最后返回了response
  5. 再回头看wsgi_app,都连接起来了。wsgi_app最后将response返回给WSGI serverWSGI serverresponse返回到客户端,一个请求的处理就完成了。
    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.  
        """
        response = self.make_response(rv)
        try:
            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

参考文章:

  1. flask 源码解析:应用启动流程
  2. Flask源码解读 — 浅谈Flask基本工作流程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值