Flask的處理流程

一、簡單Flask程序

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

二、Flask運行流程

2.一、實例化Flask類生產app對象

from flask import Flask

app = Flask(__name__)

2.二、註冊url,路由,endpoint到內存中

@app.route('/')
def hello_world():
    return 'Hello World!'

 

2.2.一、class Flask(_PackageBoundObject) 類中route()方法

def route(self, rule, **options):
    def decorator(f):
        endpoint = options.pop('endpoint', None)
        self.add_url_rule(rule, endpoint, f, **options)
        return f
    return decorator

2.2.二、class Flask(_PackageBoundObject) 類中add_url_rule()方法

def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    if endpoint is None:
        endpoint = _endpoint_from_view_func(view_func)
    options['endpoint'] = endpoint
    methods = options.pop('methods', None)

    # if the methods are not given and the view_func object knows its
    # methods we can use that instead.  If neither exists, we go with
    # a tuple of only ``GET`` as default.
    if methods is None:
        methods = getattr(view_func, 'methods', None) or ('GET',)
    if isinstance(methods, string_types):
        raise TypeError('Allowed methods have to be iterables of strings, '
                        'for example: @app.route(..., methods=["POST"])')
    methods = set(item.upper() for item in methods)

    # Methods that should always be added
    required_methods = set(getattr(view_func, 'required_methods', ()))

    # starting with Flask 0.8 the view_func object can disable and
    # force-enable the automatic options handling.
    provide_automatic_options = getattr(view_func,
        'provide_automatic_options', None)

    if provide_automatic_options is None:
        if 'OPTIONS' not in methods:
            provide_automatic_options = True
            required_methods.add('OPTIONS')
        else:
            provide_automatic_options = False

    # Add the required methods now.
    methods |= required_methods

    rule = self.url_rule_class(rule, methods=methods, **options)
    rule.provide_automatic_options = provide_automatic_options

    self.url_map.add(rule)
    if view_func is not None:
        old_func = self.view_functions.get(endpoint)
        if old_func is not None and old_func != view_func:
            raise AssertionError('View function mapping is overwriting an '
                                 'existing endpoint function: %s' % endpoint)
        self.view_functions[endpoint] = view_func

 

  執行完以後,將url放入app.url_map中;將視圖函數放入app.view_functions中:shell

2.三、運行app.run(),檢測客戶端請求,並做出響應

if __name__ == '__main__':
    app.run()

 

2.3.一、執行Flask類中的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:
        run_simple(host, port, self, **options)
    finally:
        # reset the first request information if the development server
        # reset normally.  This makes it possible to restart the server
        # without reloader and that stuff from an interactive shell.
        self._got_first_request = False

 

  try以前的代碼都是配置host、port、debug等參數。try中調用wsgi工具包werkzeug中的runsimple函數,用來創建socket連接,監聽客戶端數據,並進行解析處理,而後經過路由匹配找到視圖函數,將數據交給視圖處理後,再進行打包發給客戶端。最後一行代碼是將app中的_got_first_request標誌置爲False。當第一次客戶端請求到來時置位True,用來觸發before_first_request裝飾器(第一次請求來在視圖函數前面執行的代碼)的運行。json

2.3.二、請求來的時候執行流程

  請求來的時候執行run_simple(host, port, self, **options)中的第三個參數,即self(),self就是app,所以執行Flask類中__call__方法。flask

  Flask類中__call__方法:app

def __call__(self, environ, start_response):
    """Shortcut for :attr:`wsgi_app`."""
    return self.wsgi_app(environ, start_response)

 2.3.三、wsgi_app中的environ,start_response

from werkzeug.wrappers import Response
from werkzeug.serving import run_simple


def run_server(environ, start_response):
    print("environ:",environ)
    print("type environ:",type(environ))
    print("start_response:", start_response)
    print("type start_response:", type(start_response))
    response = Response('hello')
    return response(environ, start_response)

if __name__ == '__main__':
    run_simple('127.0.0.1', 8000, run_server)

 

  environ的類型是:<class 'dict'>,即字典類型socket

  environ的內容以下:ide

environ: {
    'wsgi.version': (1, 0),
    'wsgi.url_scheme': 'http',
    'wsgi.input': < _io.BufferedReader name = 736 > ,
    'wsgi.errors': < _io.TextIOWrapper name = '<stderr>'
    mode = 'w'
    encoding = 'UTF-8' > ,
    'wsgi.multithread': False,
    'wsgi.multiprocess': False,
    'wsgi.run_once': False,
    'werkzeug.server.shutdown': < function WSGIRequestHandler.make_environ. < locals > .shutdown_server at 0x0000000012E1B510 > ,
    'SERVER_SOFTWARE': 'Werkzeug/0.14.1',
    'REQUEST_METHOD': 'POST',
    'SCRIPT_NAME': '',
    'PATH_INFO': '/',
    'QUERY_STRING': '',
    'REMOTE_ADDR': '127.0.0.1',
    'REMOTE_PORT': 11556,
    'SERVER_NAME': '127.0.0.1',
    'SERVER_PORT': '8000',
    'SERVER_PROTOCOL': 'HTTP/1.1',
    'CONTENT_TYPE': 'application/json',
    'HTTP_CACHE_CONTROL': 'no-cache',
    'HTTP_POSTMAN_TOKEN': 'fc8d2fdf-23c6-4461-9177-9c3965f09932',
    'HTTP_USER_AGENT': 'PostmanRuntime/7.3.0',
    'HTTP_ACCEPT': '*/*',
    'HTTP_HOST': '127.0.0.1:8000',
    'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
    'CONTENT_LENGTH': '0',
    'HTTP_CONNECTION': 'keep-alive'
}

 

   start_response的類型是:<class 'function'>,即函數類型函數

   start_response的內容:工具

start_response: <function WSGIRequestHandler.run_wsgi.<locals>.start_response at 0x0000000012E2C510>

 

2.四、Flask類中的wsgi_app()方法

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)

 

 2.五、Flask類中的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
    """
    
    # 處理第一次請求前的裝飾器before_first_requst函數
    self.try_trigger_before_first_request_functions()
    try:
        request_started.send(self)
        rv = self.preprocess_request()
        
        # 若是before_request函數們處理返回的值爲None,則進行正式處理
        if rv is None:
            rv = self.dispatch_request()
    except Exception as e:
        rv = self.handle_user_exception(e)
        
    # 使用after_response裝飾器函數對視圖函數執行後的結果進行處理
    return self.finalize_request(rv)

 

2.5.一、Flask類中的try_trigger_before_first_request_functions()方法

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
            
        # self.before_first_request_funcs = [],列表中存放函數
        for func in self.before_first_request_funcs:
            func()
        self._got_first_request = True

 

 2.5.二、Flask類中的preprocess_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)


        # 獲取before_requesth裝飾器函數並處理
    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

 

2.5.三、Flask類中的dispatch_request()方法

def dispatch_request(self):
    """Does the request dispatching.  Matches the URL and returns the
    return value of the view or error handler.  This does not have to
    be a response object.  In order to convert the return value to a
    proper response object, call :func:`make_response`.

    .. versionchanged:: 0.7
       This no longer does the exception handling, this code was
       moved to the new :meth:`full_dispatch_request`.
    """
    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
    
       # 根據rule.endpoint找到app.view_functions中對應的處理函數,而後將req.view_args傳入,
       # 進行視圖函數處理,最後將結果返回
    return self.view_functions[rule.endpoint](**req.view_args)
相關文章
相關標籤/搜索