Flask Middleware and Extensions

In Flask, middleware and extensions are two key mechanisms for enhancing and extending application functionality.

Through middleware and extensions, you can greatly enhance the functionality and flexibility of Flask applications, enabling them to meet various complex requirements.

The following details the concepts of both and how to use them.

Flask Middleware:

  • Use request hooks to insert code at different stages of request processing.
  • Create custom middleware classes to handle requests and responses.

Flask Extensions:

  • Use existing extensions (such as Flask-SQLAlchemy, Flask-WTF) to add functionality.
  • Create custom extensions to meet specific needs.

1. Flask Middleware

Flask middleware is a hook for processing requests and responses, typically used to perform operations before requests reach view functions or before responses are sent to clients. Middleware can be used for logging, request modification, response modification, and more.

1.1 Request Hooks

Request hooks allow you to insert code at different stages of request processing. Flask provides several hooks to handle different stages of the request lifecycle:

  • before_request: Executed before each request is processed.
  • after_request: Executed after each request is processed.
  • teardown_request: Executed after request processing ends, regardless of whether an exception occurs.
  • before_first_request: Executed only before the application processes the first request.

app.py file code:

Example

from flask import Flask, request

app = Flask(__name__)

@app.before_request
def before_request():
    print('Before request')

@app.after_request
def after_request(response):
    print('After request')
    return response

@app.teardown_request
def teardown_request(exception):
    print('Teardown request')

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

if __name__ == '__main__':
    app.run(debug=True)
  • @app.before_request: Print "Before request" before each request is processed.
  • @app.after_request: Print "After request" after each request is processed.
  • @app.teardown_request: Print "Teardown request" after each request is processed, regardless of whether an exception occurs.

1.2 Custom Middleware

Flask also allows you to create custom middleware classes, which can operate at various stages of request and response processing.

middleware.py file code:

Example

class CustomMiddleware:
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        def custom_start_response(status, headers):
            headers.append(('X-Custom-Header', 'Value'))
            return start_response(status, headers)

        return self.app(environ, custom_start_response)

app.py file code:

Example

from flask import Flask
from middleware import CustomMiddleware

app = Flask(__name__)

app.wsgi_app = CustomMiddleware(app.wsgi_app)

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

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

CustomMiddleware: Custom middleware class that adds a custom response header.

2. Flask Extensions

Flask extensions are plugins used to add functionality to Flask applications. Flask extensions can integrate third-party libraries, providing features such as database integration, form processing, user authentication, and more.

2.1 Common Flask extensions

  • Flask-SQLAlchemy: Integrate the SQLAlchemy ORM, making database operations more convenient.
  • Flask-WTF: Integrate the WTForms form library, simplifying form processing.
  • Flask-Login: Provide user session management and user authentication functionality.
  • Flask-Migrate: An extension for database migrations, based on Alembic.
  • Flask-Mail: Used to send emails.

2.2 Installing and using Flask extensions

Taking Flask-SQLAlchemy as an example, installation:

pip install flask-sqlalchemy

Configuration and usage, app.py file code:

Example

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.db'
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)

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

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

The SQLAlchemy extension integrates SQLAlchemy, allowing you to easily define models and interact with the database.

2.3 Creating Custom Extensions

If existing extensions do not meet your needs, you can create your own extension. Creating a custom extension typically involves defining a class that provides initialization configuration and related functionality.

myextension.py file code:

Example

class MyExtension:
    def __init__(self, app=None):
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        app.config.setdefault('MY_EXTENSION_CONFIG', 'default_value')
        app.after_request(self.after_request)

    def after_request(self, response):
        response.headers['X-My-Extension'] = 'MyValue'
        return response

app.py file code:

Example

from flask import Flask
from myextension import MyExtension

app = Flask(__name__)
my_ext = MyExtension(app)

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

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

MyExtension: Custom extension that adds a custom response header.

other extensions