Обслуживание интерфейса, созданного с помощью приложения create-react-app, с помощью Flask

У меня есть серверная часть Flask с маршрутами API, к которым обращается одностраничное приложение React, созданное с помощью create-response-app. При использовании сервера разработки create-response-app мой бэкэнд Flask работает.

Я хотел бы обслуживать созданное (использующее npm run build) статическое приложение React с моего сервера Flask. Сборка приложения React приводит к следующей структуре каталогов:

- 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]

Под [crypto] я имею в виду случайно сгенерированные строки, созданные во время сборки.

После получения файла index.html браузер выполняет следующие запросы:

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

Как мне обслуживать эти файлы? Я придумал это:

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')

Таким образом, статические ресурсы успешно обслуживаются.

С другой стороны, я мог бы включить это со встроенными статическими утилитами Flask. Но я не понимаю, как это настроить.

Достаточно ли надежно мое решение? Есть ли способ использовать встроенные функции Flask для обслуживания этих ресурсов? Есть ли лучший способ использовать приложение create-response-app?


person Theo    schedule 26.05.2017    source источник
comment
flask должен знать о вашей статической папке без необходимости что-либо делать (если папка называется static и находится рядом с вашей точкой входа во флягу) ... т.е. cp -rf /build/static ./static как часть вашего скрипта сборки ...   -  person Joran Beasley    schedule 27.05.2017
comment
вы также можете использовать nginx для обслуживания ваших статических файлов, что обычно рекомендуется через (nginx очень хорош для статических файлов)   -  person patrick    schedule 28.06.2017


Ответы (5)


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)

Вот что у меня получилось. Итак, в основном поймайте все маршруты, проверьте, является ли путь файлом => отправить файл => иначе отправьте index.html. Таким образом, вы можете перезагрузить приложение React с любого маршрута, и оно не сломается.

person Jodo    schedule 11.08.2017
comment
Я получаю ошибки типа MIME с этим решением :-( 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'). - person ketysek; 09.03.2018
comment
@ user3216673, скорее всего, проблема не в колбе или приложении create-react-app, а в вашем браузере. Предположение: отмена регистрации обслуживающего персонала может решить проблему. - person Jodo; 04.05.2018
comment
Вы можете использовать app.static_folder, чтобы ваш код оставался СУХИМ - person Eytan; 13.04.2019

Сначала выполните npm run build, чтобы создать статические производственные файлы, как указано вами выше.

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')

К сожалению, я не думаю, что вы сможете заставить его работать с горячей перезагрузкой разработки.

person Pranay Aryal    schedule 11.01.2019

Здесь рабочее решение. Вы когда-нибудь задумывались, зачем нам две отдельные папки для static и templates. Чтобы отделить беспорядок, верно? Но это проблема производственной сборки, поскольку в ней есть одна папка для файлов типа static и templates, и все зависимости связаны таким образом.

Папка build будет обслуживаться, если рассматривать ее одновременно как static, так и templates.

Используйте что-то вроде этого

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")

Ваше приложение Flask будет работать нормально.

person pankaj    schedule 26.02.2020
comment
Этот ответ меня спас. У всех остальных были некоторые предостережения, которые не подходили под мою установку. - person user362178; 09.03.2020

Принятый ответ мне не подходит. я использовал

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


person Lukasz Madon    schedule 25.08.2019

Я использовал фляжный сервер только с одним route /, который считывал файл index.html из папки сборки приложения Create response (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()

При такой настройке я столкнулся с ошибкой, статические файлы не обслуживаются должным образом из-за несоответствия пути, поэтому я использовал решение

  1. Добавьте свойство домашней страницы в package.json CRA и установите для него значение / static.

{name: Имя приложения, версия :, зависимости: {} домашняя страница: / static, .... [другие ключи]}

 Add **homepage** key parallel to the **dependencies** key in the package.json file
  1. Это свойство домашняя страница будет использоваться во время процесса сборки CRA и использоваться вместо% PUBLIC_URL% в index.html и добавляется к пути URL-адреса других статических ресурсов (вы можете проверить, просмотрев index. html-код после процесса сборки)

  2. После процесса сборки запустите флеш-сервер, мы увидим, что GET-запрос поступает с / в первый раз, а index.html будет обслуживаться, а за ним следуют запросы / static / static / js / [[filename]] для других статических ресурсов из файла HTML, и все работает правильно

person sathishkumar    schedule 20.04.2021