Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How are Requests Processed in Flask?

Patrick
December 09, 2023

How are Requests Processed in Flask?

When developing web applications with Flask, most people focus on writing routes (i.e. view functions) to generate a response, but there is so much that happens before and after a route method is executed. This talk provides a visual demonstration of how requests are processed by a Flask application, by focusing on what happens before and after a view function is executed, including contexts (application and request), callbacks, sessions, and error handling.

Patrick

December 09, 2023
Tweet

More Decks by Patrick

Other Decks in Technology

Transcript

  1. Goal: • Teach the steps of how a request is

    processed in Flask • Highlight callbacks to help you power-up your Flask application Topics: • Request / Response Cycle • What happens before a view function is executed? • What happens after a view function is executed? • Key callbacks to utilize in Flask applications 2 How are Requests Processed in Flask?
  2. First point Web Server WSGI Server Flask Application Web Browser

    (Firefox, Chrome, etc.) http://www.my-flask-app.com/ Request: GET ‘/’ 200 (OK) Status Code Response: “Hello World!” 3 Request / Response Cycle
  3. Web Server WSGI Server Flask Application Request: GET ‘/’ 200

    (OK) Status Code Response: “Hello World!” 4 @app.get(‘/’) def index(): return “Hello World!” Request / Response Cycle View Function
  4. BEFORE and AFTER a view function 5 View Function Request

    Object created Application Context pushed Request Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` `errorhandler` `after_this_request` `after_request` Session saved Response object returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped
  5. 6 View Function Request Object created Application Context pushed Request

    Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` The steps prior to the view function are to prepare for the necessary data and contexts to be available for the view function. Before a View Function: Overview
  6. 7 View Function Request Object created Application Context pushed Request

    Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` • `environ` object passed from the WSGI server to the Flask application (Flask.wsgi_app()) is converted to a `Request` object Before a View Function: Step 1
  7. 8 View Function Request Object created Application Context pushed Request

    Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` • The Application Context is pushed onto the stack: ◦ keeps track of the application-level data (configuration variables, logger, etc.) • This step makes the following objects available: ◦ `current_app` - proxy to the application during the application context ◦ `g` - stores “global” data during the application context Before a View Function: Step 2
  8. 9 View Function Request Object created Application Context pushed Request

    Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` • The Request Context is pushed onto the stack: ◦ keeps track of the request-level data (URL, HTTP method, headers, request data, session, etc.) • This step makes the following objects available: ◦ `request` - proxy to the request data passed in from the WSGI server ◦ `session` - data for storing data from one request to another (i.e. a session) Before a View Function: Step 3
  9. 10 View Function Request Object created Application Context pushed Request

    Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` • Session data is loaded via the `session_interface`, which allows either: ◦ Client-side sessions - default for Flask ◦ Server-side sessions - implemented via Flask extensions (i.e. Flask-Session) Before a View Function: Step 4
  10. 11 View Function Request Object created Application Context pushed Request

    Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` • URL matching involves trying to find the correct view function (decorated with `route()` or shortcut decorators) • If there is no match, then an error is stored for processing later (after the `url_value_preprocessor` and `before_request` decorated functions are executed) Before a View Function: Step 5
  11. 12 View Function Request Object created Application Context pushed Request

    Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` • Functions decorated with `url_value_preprocessor` are executed • Beneficial for modifying the URL values to be passed to the view function Examples of `url_value_preprocessor` usage are presented in a later slide Before a View Function: Step 6
  12. 13 View Function Request Object created Application Context pushed Request

    Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` • Functions decorated with `before_request` are executed • Beneficial for loading necessary data needed by the view function: ◦ Database connection ◦ Loading user data Examples of `before_request` usage are presented in a later slide Before a View Function: Step 7
  13. 14 View Function `errorhandler` `after_this_request` `after_request` Session saved Response object

    returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped The steps after the view function are to generate the response and clean-up the contexts. After a View Function: Overview
  14. 15 View Function `errorhandler` `after_this_request` `after_request` Session saved Response object

    returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped • If an error has been generated and there is a matching `errorhandler` decorated function, then it gets executed here Examples of `errorhandler` usage are presented in a later slide After a View Function: Step 1
  15. 16 View Function `errorhandler` `after_this_request` `after_request` Session saved Response object

    returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped • Functions decorated with `after_this_request` are executed • Beneficial for modifying the response, but only in certain situations Examples of `after_this_request` usage are presented in a later slide After a View Function: Step 2
  16. 17 View Function `errorhandler` `after_this_request` `after_request` Session saved Response object

    returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped • Functions decorated with `after_request` are executed • Beneficial for modifying the response ◦ Executed for each request processed Examples of `after_request` usage are presented in a later slide After a View Function: Step 3
  17. 18 View Function `errorhandler` `after_this_request` `after_request` Session saved Response object

    returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped • Session data is saved for use in the next request processed After a View Function: Step 4
  18. 19 View Function `errorhandler` `after_this_request` `after_request` Session saved Response object

    returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped • `Response` object is returned to the WSGI server • Notice that the response is returned after any calls to `after_request()` decorated functions, but before any calls to `teardown_*()` decorated functions. After a View Function: Step 5
  19. 20 View Function `errorhandler` `after_this_request` `after_request` Session saved Response object

    returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped • Functions decorated with `teardown_request` are executed • Useful for cleaning up resources after a request is done being processed, such as database connections Examples of `teardown_request` usage are presented in a later slide After a View Function: Step 6
  20. 21 View Function `errorhandler` `after_this_request` `after_request` Session saved Response object

    returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped • The Request Context is popped from the stack: ◦ `request` and `session` are no longer available After a View Function: Step 7
  21. 22 View Function `errorhandler` `after_this_request` `after_request` Session saved Response object

    returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped • Functions decorated with `teardown_appcontext` are executed • Useful for cleaning up resources after a request is done being processed After a View Function: Step 8 Examples of `teardown_appcontext` usage are presented in a later slide
  22. 23 View Function `errorhandler` `after_this_request` `after_request` Session saved Response object

    returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped • The Application Context is popped from the stack: ◦ `current_app` and `g` are no longer available After a View Function: Step 9
  23. 24 View Function Request Object created Application Context pushed Request

    Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` `errorhandler` `after_this_request` `after_request` Session saved Response object returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped Callbacks in Flask
  24. Example: Reading the username from the URL for a social

    media application: http://www.flask-social.com/patrick123 25 Purpose: Read or modify the URL information prior to the view function executing Flask documentation shows how to use `url_value_preprocessor` to read the language code in a URL: https://flask.palletsprojects.com/en/3.0.x/patterns/urlprocessors/ @app.url_value_preprocessor def get_site(endpoint, values): g.user = values.pop('username', None) @app.route('/<username>') def profile_page(): # `g` available in templates too! return f"<h1>Profile Page - {g.user}</h1>" url_value_preprocessor
  25. Example #1: Check if the user is authorized to access

    the route; abort with a 403 (Forbidden) error if not authorized 26 Purpose: Execute a function before each call to a view function Flask-WTForms uses a `before_request()` callback to check the CSRF token: https://github.com/wtforms/flask-wtf/blob/main/src/flask_wtf/csrf.py @admin_blueprint.before_request def admin_before_request(): if current_user.user_type != 'Admin': abort(403) @app.before_request def database_connection(): if 'db' not in g: g.db = sqlite3.connect(app.config[‘SQLITE_FILE’]) Example #2: Create a connection to a SQLite database Returning a non-None value causes the view function to NOT be executed! before_request
  26. Example: Custom error pages for 403 (Forbidden) and 404 (Page

    Not Found) 27 Purpose: gracefully handle specific HTTP error codes @app.errorhandler(403) def page_forbidden(e): return render_template('403.html'), 403 @app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404 errorhandler
  27. Example: Setting a cookie (or alternatively modifying the header in

    the response) when a specific view function is executed 28 Purpose: execute a function to modify the response, but only after specific requests from flask import after_this_request @app.route('/profile') def profile(): @after_this_request def remember_theme(response): response.set_cookie(‘theme’, theme) return response return render_template(‘profile.html’) after_this_request
  28. Example: Modifying the header in the response after every request

    is processed 29 Purpose: execute a function to modify the response; called after every request @app.after_request def remember_language(response): response.headers[‘language’] = g.language return response after_request Flask-Login uses an `after_request()` callback to handle the “Remember Me” cookie: https://github.com/maxcountryman/flask-login/blob/main/src/flask_login/login_manager.py
  29. Example: Closing the connection to a SQLite database 30 Purpose:

    cleaning up resources after a request has been processed (i.e. response has been returned) @app.teardown_appcontext def close_database_connection(error): database = g.pop('database', None) if database is not None: database.close() Callback Contexts Available teardown_request request, session, current_app, g teardown_appcontext current_app, g teardown_request / teardown_appcontext
  30. • There are lots of steps before and after a

    view function is called in Flask ◦ Beneficial to understand these steps when developing Flask applications • Callbacks can be utilized to power-up your Flask application: ◦ url_value_preprocessor ◦ before_request ◦ error_handler ◦ after_this_request ◦ after_request ◦ teardown_request ◦ teardown_appcontext 31 Conclusion
  31. Trivia Question 34 View Function Request Object created Application Context

    pushed Request Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` `errorhandler` `after_this_request` `after_request` Session saved Response object returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped What happens if a `before_request` decorated function calls `abort(403)` for an unauthorized user? abort(403)
  32. Trivia Question 35 View Function Request Object created Application Context

    pushed Request Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` `errorhandler` `after_this_request` `after_request` Session saved Response object returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped What happens if a `before_request` decorated function calls `abort(403)` for an unauthorized user? abort(403) Scenario #1: `errorhandler` decorated function specified for 403 error code
  33. Trivia Question 36 View Function Request Object created Application Context

    pushed Request Context pushed Session Opened URL Matching `before_request` `url_value_preprocessor` `errorhandler` `after_this_request` `after_request` Session saved Response object returned `teardown_request` Request Context popped `teardown_appcontext` Application Context popped What happens if a `before_request` decorated function calls `abort(403)` for an unauthorized user? abort(403) Scenario #2: No `errorhandler` available Exception handled