|
| 1 | +Application Structure and Lifecycle |
| 2 | +=================================== |
| 3 | + |
| 4 | +Flask makes it pretty easy to write a web application. But there are quite a few |
| 5 | +different parts to an application and to each request it handles. Knowing what happens |
| 6 | +during application setup, serving, and handling requests will help you know what's |
| 7 | +possible in Flask and how to structure your application. |
| 8 | + |
| 9 | + |
| 10 | +Application Setup |
| 11 | +----------------- |
| 12 | + |
| 13 | +The first step in creating a Flask application is creating the application object. Each |
| 14 | +Flask application is an instance of the :class:`.Flask` class, which collects all |
| 15 | +configuration, extensions, and views. |
| 16 | + |
| 17 | +.. code-block:: python |
| 18 | +
|
| 19 | + from flask import Flask |
| 20 | +
|
| 21 | + app = Flask(__name__) |
| 22 | + app.config.from_mapping( |
| 23 | + SECRET_KEY="dev", |
| 24 | + ) |
| 25 | + app.config.from_prefixed_env() |
| 26 | +
|
| 27 | + @app.route("/") |
| 28 | + def index(): |
| 29 | + return "Hello, World!" |
| 30 | +
|
| 31 | +This is known as the "application setup phase", it's the code you write that's outside |
| 32 | +any view functions or other handlers. It can be split up between different modules and |
| 33 | +sub-packages, but all code that you want to be part of your application must be imported |
| 34 | +in order for it to be registered. |
| 35 | + |
| 36 | +All application setup must be completed before you start serving your application and |
| 37 | +handling requests. This is because WSGI servers divide work between multiple workers, or |
| 38 | +can be distributed across multiple machines. If the configuration changed in one worker, |
| 39 | +there's no way for Flask to ensure consistency between other workers. |
| 40 | + |
| 41 | +Flask tries to help developers catch some of these setup ordering issues by showing an |
| 42 | +error if setup-related methods are called after requests are handled. In that case |
| 43 | +you'll see this error: |
| 44 | + |
| 45 | + The setup method 'route' can no longer be called on the application. It has already |
| 46 | + handled its first request, any changes will not be applied consistently. |
| 47 | + Make sure all imports, decorators, functions, etc. needed to set up the application |
| 48 | + are done before running it. |
| 49 | + |
| 50 | +However, it is not possible for Flask to detect all cases of out-of-order setup. In |
| 51 | +general, don't do anything to modify the ``Flask`` app object and ``Blueprint`` objects |
| 52 | +from within view functions that run during requests. This includes: |
| 53 | + |
| 54 | +- Adding routes, view functions, and other request handlers with ``@app.route``, |
| 55 | + ``@app.errorhandler``, ``@app.before_request``, etc. |
| 56 | +- Registering blueprints. |
| 57 | +- Loading configuration with ``app.config``. |
| 58 | +- Setting up the Jinja template environment with ``app.jinja_env``. |
| 59 | +- Setting a session interface, instead of the default itsdangerous cookie. |
| 60 | +- Setting a JSON provider with ``app.json``, instead of the default provider. |
| 61 | +- Creating and initializing Flask extensions. |
| 62 | + |
| 63 | + |
| 64 | +Serving the Application |
| 65 | +----------------------- |
| 66 | + |
| 67 | +Flask is a WSGI application framework. The other half of WSGI is the WSGI server. During |
| 68 | +development, Flask, through Werkzeug, provides a development WSGI server with the |
| 69 | +``flask run`` CLI command. When you are done with development, use a production server |
| 70 | +to serve your application, see :doc:`deploying/index`. |
| 71 | + |
| 72 | +Regardless of what server you're using, it will follow the :pep:`3333` WSGI spec. The |
| 73 | +WSGI server will be told how to access your Flask application object, which is the WSGI |
| 74 | +application. Then it will start listening for HTTP requests, translate the request data |
| 75 | +into a WSGI environ, and call the WSGI application with that data. The WSGI application |
| 76 | +will return data that is translated into an HTTP response. |
| 77 | + |
| 78 | +#. Browser or other client makes HTTP request. |
| 79 | +#. WSGI server receives request. |
| 80 | +#. WSGI server converts HTTP data to WSGI ``environ`` dict. |
| 81 | +#. WSGI server calls WSGI application with the ``environ``. |
| 82 | +#. Flask, the WSGI application, does all its internal processing to route the request |
| 83 | + to a view function, handle errors, etc. |
| 84 | +#. Flask translates View function return into WSGI response data, passes it to WSGI |
| 85 | + server. |
| 86 | +#. WSGI server creates and send an HTTP response. |
| 87 | +#. Client receives the HTTP response. |
| 88 | + |
| 89 | + |
| 90 | +Middleware |
| 91 | +~~~~~~~~~~ |
| 92 | + |
| 93 | +The WSGI application above is a callable that behaves in a certain way. Middleware |
| 94 | +is a WSGI application that wraps another WSGI application. It's a similar concept to |
| 95 | +Python decorators. The outermost middleware will be called by the server. It can modify |
| 96 | +the data passed to it, then call the WSGI application (or further middleware) that it |
| 97 | +wraps, and so on. And it can take the return value of that call and modify it further. |
| 98 | + |
| 99 | +From the WSGI server's perspective, there is one WSGI application, the one it calls |
| 100 | +directly. Typically, Flask is the "real" application at the end of the chain of |
| 101 | +middleware. But even Flask can call further WSGI applications, although that's an |
| 102 | +advanced, uncommon use case. |
| 103 | + |
| 104 | +A common middleware you'll see used with Flask is Werkzeug's |
| 105 | +:class:`~werkzeug.middleware.proxy_fix.ProxyFix`, which modifies the request to look |
| 106 | +like it came directly from a client even if it passed through HTTP proxies on the way. |
| 107 | +There are other middleware that can handle serving static files, authentication, etc. |
| 108 | + |
| 109 | + |
| 110 | +How a Request is Handled |
| 111 | +------------------------ |
| 112 | + |
| 113 | +For us, the interesting part of the steps above is when Flask gets called by the WSGI |
| 114 | +server (or middleware). At that point, it will do quite a lot to handle the request and |
| 115 | +generate the response. At the most basic, it will match the URL to a view function, call |
| 116 | +the view function, and pass the return value back to the server. But there are many more |
| 117 | +parts that you can use to customize its behavior. |
| 118 | + |
| 119 | +#. WSGI server calls the Flask object, which calls :meth:`.Flask.wsgi_app`. |
| 120 | +#. A :class:`.RequestContext` object is created. This converts the WSGI ``environ`` |
| 121 | + dict into a :class:`.Request` object. It also creates an :class:`AppContext` object. |
| 122 | +#. The :doc:`app context <appcontext>` is pushed, which makes :data:`.current_app` and |
| 123 | + :data:`.g` available. |
| 124 | +#. The :data:`.appcontext_pushed` signal is sent. |
| 125 | +#. The :doc:`request context <reqcontext>` is pushed, which makes :attr:`.request` and |
| 126 | + :class:`.session` available. |
| 127 | +#. The session is opened, loading any existing session data using the app's |
| 128 | + :attr:`~.Flask.session_interface`, an instance of :class:`.SessionInterface`. |
| 129 | +#. The URL is matched against the URL rules registered with the :meth:`~.Flask.route` |
| 130 | + decorator during application setup. If there is no match, the error - usually a 404, |
| 131 | + 405, or redirect - is stored to be handled later. |
| 132 | +#. The :data:`.request_started` signal is sent. |
| 133 | +#. Any :meth:`~.Flask.url_value_preprocessor` decorated functions are called. |
| 134 | +#. Any :meth:`~.Flask.before_request` decorated functions are called. If any of |
| 135 | + these function returns a value it is treated as the response immediately. |
| 136 | +#. If the URL didn't match a route a few steps ago, that error is raised now. |
| 137 | +#. The :meth:`~.Flask.route` decorated view function associated with the matched URL |
| 138 | + is called and returns a value to be used as the response. |
| 139 | +#. If any step so far raised an exception, and there is an :meth:`~.Flask.errorhandler` |
| 140 | + decorated function that matches the exception class or HTTP error code, it is |
| 141 | + called to handle the error and return a response. |
| 142 | +#. Whatever returned a response value - a before request function, the view, or an |
| 143 | + error handler, that value is converted to a :class:`.Response` object. |
| 144 | +#. Any :func:`~.after_this_request` decorated functions are called, then cleared. |
| 145 | +#. Any :meth:`~.Flask.after_request` decorated functions are called, which can modify |
| 146 | + the response object. |
| 147 | +#. The session is saved, persisting any modified session data using the app's |
| 148 | + :attr:`~.Flask.session_interface`. |
| 149 | +#. The :data:`.request_finished` signal is sent. |
| 150 | +#. If any step so far raised an exception, and it was not handled by an error handler |
| 151 | + function, it is handled now. HTTP exceptions are treated as responses with their |
| 152 | + corresponding status code, other exceptions are converted to a generic 500 response. |
| 153 | + The :data:`.got_request_exception` signal is sent. |
| 154 | +#. The response object's status, headers, and body are returned to the WSGI server. |
| 155 | +#. Any :meth:`~.Flask.teardown_request` decorated functions are called. |
| 156 | +#. The :data:`.request_tearing_down` signal is sent. |
| 157 | +#. The request context is popped, :attr:`.request` and :class:`.session` are no longer |
| 158 | + available. |
| 159 | +#. Any :meth:`~.Flask.teardown_appcontext` decorated functions are called. |
| 160 | +#. The :data:`.appcontext_tearing_down` signal is sent. |
| 161 | +#. The app context is popped, :data:`.current_app` and :data:`.g` are no longer |
| 162 | + available. |
| 163 | +#. The :data:`.appcontext_popped` signal is sent. |
| 164 | + |
| 165 | +There are even more decorators and customization points than this, but that aren't part |
| 166 | +of every request lifecycle. They're more specific to certain things you might use during |
| 167 | +a request, such as templates, building URLs, or handling JSON data. See the rest of this |
| 168 | +documentation, as well as the :doc:`api` to explore further. |
0 commit comments