Search code examples
pythonflaskroutesmiddleware

Flask middleware for specific route


I made API Server with Python Flask-RESTful.

My system use token authentication for verify permission.

So, I added middleware for verify token.

For example, code like this,

[middleware.py]

class Test(object):
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        print("gogo")
        return self.app(environ, start_response)

[app.py]

from flask import Flask
from flask_restful import Api
from api.board import Article
from api.auth import Login, Register, RefreshToken
from middleware import Test


app = Flask(__name__)
api = Api(app)

api.add_resource(Login, '/login')
api.add_resource(Register, '/register')
api.add_resource(RefreshToken, '/refresh')

# middleware here
app.wsgi_app = Test(app.wsgi_app)
api.add_resource(Article, '/article')

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

I insert app.wsgi_app = Test(app.wsgi_app) before /article.

So I expect that only access to /article will print "gogo", however every route print "gogo".

Maybe every route pass through with middleware.

How can I apply middleware for specific route? (In this code, only /article)


Solution

  • There are a few ways how to add custom processing before specific endpoint.

    1) Using python decorator:

    from functools import wraps
    
    def home_decorator():
        def _home_decorator(f):
            @wraps(f)
            def __home_decorator(*args, **kwargs):
                # just do here everything what you need
                print('before home')
                result = f(*args, **kwargs)
                print('home result: %s' % result)
                print('after home')
                return result
            return __home_decorator
        return _home_decorator
    
    
    @app.route('/home')
    @home_decorator()
    def home():
        return 'Hello'
    

    2) Using before_request

    @app.before_request
    def hook():
        # request - flask.request
        print('endpoint: %s, url: %s, path: %s' % (
            request.endpoint,
            request.url,
            request.path))
        # just do here everything what you need...
    

    3) Using middleware. Just add condition on request path.

    class Middleware:
    
        def __init__(self, app):
            self.app = app
    
        def __call__(self, environ, start_response):
            # not Flask request - from werkzeug.wrappers import Request
            request = Request(environ)
            print('path: %s, url: %s' % (request.path, request.url))
            # just do here everything what you need
            return self.app(environ, start_response)
    
    
    @app.route('/home')
    def home():
        return 'Hello'
    
    
    app.wsgi_app = Middleware(app.wsgi_app)
    

    4) Also you can use before_request_funcs to set list of functions before specific blueprint.

    api = Blueprint('my_blueprint', __name__)
    
    
    def before_my_blueprint():
        print(111)
    
    
    @api.route('/test')
    def test():
        return 'hi'
    
    
    app.before_request_funcs = {
        # blueprint name: [list_of_functions]
        'my_blueprint': [before_my_blueprint]
    }
    
    app.register_blueprint(api)