Search code examples
pythonflaskwsgi

How do I access response content in WSGI middleware for Flask?


I want to write middleware for Flask that changes the data in an HTML response. In Django, I could use a Middleware class and access response.content to change the content. How can I access and modify the response content in Flask or WSGI middleware?

class Middleware:
    def __init__(self, get_response=None):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        self.process_response(request, response)
        return response

    def process_response(self, request, response):
        html = response.content.decode('utf-8')
        # do stuff with the HTML
        return response

Solution

  • The closest equivalent of Django's "middleware" in Flask is Flask's before_request and after_request functions. They can introspect and modify the request and response at the same layer as the Flask app.

    To get the response content, access response.get_data(as_text=True) in an after_request function. The function must return a response, either modify response.data directly or create a new response with app.make_response(new_data). You can also modify any other properties of the resonse.

    from flask import request
    
    @app.before_request
    def modify_incoming():
        if {some test on request}:
            ...
    
        # can return app.make_response() to end the request early
    
    @app.after_request
    def modify_outgoing(response):
        html = response.get_data(as_text=True)
        response.data = {modify html}
        return response
    

    WSGI middleware (which can be used with any WSGI framework, including Django) is at a layer outside the app itself, so you would need to either use the basic interface provided by the WSGI spec. Werkzeug's utilities may be useful at this lower layer.

    class ModifyHTMLMiddleware:
        def __init__(self, app):
            self.app = app
    
        def __call__(self, environ, start_response):
            # can modify environ to affect what the wrapped application sees
            app_iter = self.app(environ, start_response)
            # decode and encode need to use the appropriate codec
            html = b"".join(app_iter).decode("utf8")
            # modify html
            return [html.encode("utf8")]