Search code examples
pythonflaskmicroservices

How do I forward responses from microservices back to the client?


TL/DR: Inside a flask endpoint, I'm making a request to a different endpoint and I want to return the response of that request. The response is a file attachment, how do I correctly return that response? response.content won't cut it (displays file binaries in browser rather than downloading file)


Details below:


I've been looking around for solutions to this online, but I can't seem to find anyone with the same problem, which could suggest I might be going about this wrong. Here's the breakdown:

I've got a backend running a few flask microservices, one of which is the main gateway that interacts with the frontend. Part of my app involves the user accessing a specific link that returns a file download. With a bit of research, I found that doing this in flask is quite straightforward with the use of send_file() or send_from_directory(). The problem I'm facing is that I've put all filestorage-related activity into its own microservice, and I can't figure out how to "forward" the response I get from that microservice back to the client from the gateway.

So in essence, I've got:

filestorage_service: app.py:

# flask stuff: imports, app config, etc

@app.route('/get', methods=['GET'])
def get_file():
    filename = request.form.get('filename')

    try:
        return send_from_directory("<some_base_dir>", filename=filename, as_attachment=True)

    except FileNotFoundError:
        return "Error: File not found", 400

gateway: app.py:

# flask stuff: imports, app config, etc

@app.route('/get-file/<filename>', methods=['GET'])
@cross_origin()
def get_file(filename):
    data = {
        'filename': filename
    }

    res = requests.get("<filestorage_service_url>" + "/get", data=data)
    
    # now what??
    

How do I go about returning res? Simply putting return res gives an error, and returning res.content actually displays the image binaries in the browser. Should I be going full proxy mode and rebuild the entire response from scratch, or is there an easier way that I'm missing?


Solution

  • I managed to get a stripped down version of your app to do what you want.

    import os
    
    import requests
    from flask import Flask, send_from_directory, Response
    
    app = Flask(__name__)
    
    # Storage service
    @app.route('/get', methods=['GET'])
    def get_file():
        filename = 'somefile.txt'
    
        try:
            return send_from_directory(os.getcwd(), filename=filename, as_attachment=True)
    
        except FileNotFoundError:
            return "Error: File not found", 400
    
    # Front end
    @app.route('/getfile', methods=['GET'])
    def get_file_1():
        res = requests.get("http://127.0.0.1:5000" + "/get",)  # call storage
        return Response(
            res.content,  # content fromthe storage service has the file data
            headers=dict(res.headers)  # headers need to copied to start download
        )
    
    
    app.run(debug=True)
    

    All you need to do is make sure that the content & headers are copied from the response you want to forward. The important headers are the Content-Disposition, Content-Type & Content-Length headers. The file contents themselves are in response.content