98 votes

Servir un front-end créé avec create-react-app avec Flask

J'ai un back-end Flask avec des routes API auxquelles on accède par une application React à page unique créée à l'aide de create-react-app. Lorsque j'utilise le serveur de développement create-react-app, mon back-end Flask fonctionne.

Je voudrais servir le construit (en utilisant npm run build ) application React statique depuis mon serveur Flask. La construction de l'application React conduit à la structure de répertoire suivante :

- build
  - static
    - css
        - style.[crypto].css
        - style.[crypto].css.map
    - js
        - main.[crypto].js
        - main.[crypto].js.map
  - index.html
  - service-worker.js
  - [more meta files]

Par [crypto] Je veux dire les chaînes générées aléatoirement au moment de la construction.

Ayant reçu le index.html le navigateur effectue alors les requêtes suivantes :

- GET /static/css/main.[crypto].css
- GET /static/css/main.[crypto].css
- GET /service-worker.js

Comment dois-je servir ces fichiers ? J'ai trouvé ça :

from flask import Blueprint, send_from_directory

static = Blueprint('static', __name__)

@static.route('/')
def serve_static_index():
    return send_from_directory('../client/build/', 'index.html')

@static.route('/static/<path:path>') # serve whatever the client requested in the static folder
def serve_static(path):
    return send_from_directory('../client/build/static/', path)

@static.route('/service-worker.js')
def serve_worker():
    return send_from_directory('../client/build/', 'service-worker.js')

De cette façon, les actifs statiques sont servis avec succès.

D'un autre côté, je pourrais incorporer cela avec les utilitaires statiques intégrés de Flask. Mais je ne comprends pas comment configurer cela.

Ma solution est-elle suffisamment robuste ? Y a-t-il un moyen d'utiliser les fonctions intégrées de Flask pour servir ces ressources ? Y a-t-il un meilleur moyen d'utiliser create-react-app ?

4 votes

Flask devrait connaître votre dossier statique sans que vous ayez à faire quoi que ce soit (tant que le dossier est nommé static et se trouve à côté de votre point d'entrée flask) ... c'est à dire cp -rf /build/static ./static dans le cadre de votre construction script ...

1 votes

Vous pouvez également utiliser nginx pour servir vos fichiers statiques, ce qui est généralement le via recommandé (nginx est super bon pour les fichiers statiques)

114voto

Jodo Points 1356
import os
from flask import Flask, send_from_directory

app = Flask(__name__, static_folder='react_app/build')

# Serve React App
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def serve(path):
    if path != "" and os.path.exists(app.static_folder + '/' + path):
        return send_from_directory(app.static_folder, path)
    else:
        return send_from_directory(app.static_folder, 'index.html')

if __name__ == '__main__':
    app.run(use_reloader=True, port=5000, threaded=True)

C'est ce que j'ai fini par faire. En gros, il faut attraper toutes les routes, tester si le chemin est un fichier => envoyer le fichier => sinon envoyer l'index.html. De cette façon, vous pouvez recharger l'application react à partir de n'importe quelle route que vous souhaitez et il ne casse pas.

3 votes

J'obtiens des erreurs de type mime avec cette solution :-( The script has an unsupported MIME type ('text/html'). /service-worker.js Failed to load resource: net::ERR_INSECURE_RESPONSE registerServiceWorker.js:71 Error during service worker registration: DOMException: Failed to register a ServiceWorker: The script has an unsupported MIME type ('text/html').

0 votes

@user3216673 il est fort probable que ce ne soit pas un problème avec flask ou create-react-app mais avec votre navigateur. Une supposition dans la nature : désenregistrer vos travailleurs de service pourrait résoudre le problème.

1 votes

Vous pouvez utiliser app.static_folder pour conserver votre code DRY.

22voto

pankaj Points 372

Une solution fonctionnelle ici. Vous vous êtes déjà demandé pourquoi nous avons besoin de deux dossiers séparés pour static y templates . Pour séparer le désordre, non ? Mais, c'est un problème avec la version de production puisqu'il y a un dossier pour les deux static y templates et toutes les dépendances sont liées de cette manière.

En build Le dossier sera servi si vous le considérez à la fois static y templates .

Utilisez quelque chose comme ceci

from flask import Flask, render_template

app = Flask(__name__, static_url_path='',
                  static_folder='build',
                  template_folder='build')

@app.route("/")
def hello():
    return render_template("index.html")

Votre application Flask fonctionnera bien.

19voto

Pranay Aryal Points 2298

D'abord faire npm run build pour construire les fichiers de production statiques comme mentionné par vous ci-dessus

from flask import Flask, render_template

app = Flask(__name__, static_folder="build/static", template_folder="build")

@app.route("/")
def hello():
    return render_template('index.html')

print('Starting Flask!')

app.debug=True
app.run(host='0.0.0.0')

Malheureusement, je ne pense pas que vous puissiez le faire fonctionner avec le hot-reload de développement.

6voto

lukas Points 7789

La réponse acceptée ne fonctionne pas pour moi. J'ai utilisé

import os

from flask import Flask, send_from_directory, jsonify, render_template, request

from server.landing import landing as landing_bp
from server.api import api as api_bp

app = Flask(__name__, static_folder="../client/build")
app.register_blueprint(landing_bp, url_prefix="/landing")
app.register_blueprint(api_bp, url_prefix="/api/v1")

@app.route("/")
def serve():
    """serves React App"""
    return send_from_directory(app.static_folder, "index.html")

@app.route("/<path:path>")
def static_proxy(path):
    """static folder serve"""
    file_name = path.split("/")[-1]
    dir_name = os.path.join(app.static_folder, "/".join(path.split("/")[:-1]))
    return send_from_directory(dir_name, file_name)

@app.errorhandler(404)
def handle_404(e):
    if request.path.startswith("/api/"):
        return jsonify(message="Resource not found"), 404
    return send_from_directory(app.static_folder, "index.html")

@app.errorhandler(405)
def handle_405(e):
    if request.path.startswith("/api/"):
        return jsonify(message="Mehtod not allowed"), 405
    return e

0voto

sathishkumar Points 1

J'ai utilisé un serveur flask avec un seul itinéraire / qui lit le fichier index.html à partir du dossier de construction de Create react app(CRA)

from flask import Flask
app = Flask(__name__)
app.static_folder =  '../build'

@app.route('/')
def index():
    fref = open(r'../build/index.html')
    html_text = fref.read()
    fref.close()
    return html_text

app.run()

En procédant ainsi, j'ai été confronté à une erreur : les fichiers statiques ne sont pas correctement servis en raison d'une erreur de chemin, La solution que j'ai utilisée est donc

  1. Ajoutez une propriété homepage dans le package.json de CRA et définissez-la comme suit "/statique"

{ "name" : "App-name", "version" :", "dependencies":{} "homepage" : "/static",.... [autres clés]}

 Add **homepage** key parallel to the **dependencies** key in the package.json file
  1. Este page d'accueil sera utilisée pendant le processus de construction de la CRA et remplacera %PUBLIC_URL% dans le fichier index.html et sera ajoutée au chemin URL des autres ressources statiques (vous pouvez vérifier en visualisant le code index.html après le processus de construction).

  2. Après le processus de construction, exécuter le serveur flask, nous pouvons voir la demande GET venant avec / pour la première fois et index.html sera servi et suivi de demandes /statique/static/js/[[nom de fichier]] pour les autres actifs statiques du fichier HTML et tout fonctionne correctement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X