使用 Flask 为使用 create-react-app 创建的前端提供服务
Posted
技术标签:
【中文标题】使用 Flask 为使用 create-react-app 创建的前端提供服务【英文标题】:Serving a front end created with create-react-app with Flask 【发布时间】:2017-10-27 20:14:56 【问题描述】:我有一个带有 API 路由的 Flask 后端,这些路由由使用 create-react-app 创建的 React 单页应用程序访问。当使用 create-react-app 开发服务器时,我的 Flask 后端工作。
我想从我的 Flask 服务器提供构建的(使用 npm run build
)静态 React 应用程序。构建 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-react-app?
【问题讨论】:
flask 应该知道您的静态文件夹,而无需您做任何事情(只要该文件夹被命名为 static 并且在您的烧瓶入口点旁边)...即cp -rf /build/static ./static
作为您构建的一部分脚本...
你也可以使用 nginx 来服务你的静态文件,通常推荐使用 via(nginx 对静态文件超级好)
【参考方案1】:
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 应用程序并且它不会中断。
【讨论】:
我在使用这个解决方案时遇到了 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').
@user3216673 这很可能不是烧瓶或 create-react-app 的问题,而是您的浏览器的问题。猜测:取消注册您的服务人员可能会解决问题。
你可以使用 app.static_folder 来保持你的代码 DRY【参考方案2】:
首先使用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')
不幸的是,我不认为你可以使用开发热重载来让它工作。
【讨论】:
【参考方案3】:这里有一个可行的解决方案。
有没有想过为什么我们需要两个单独的文件夹来存放 static
和 templates
。把乱七八糟的东西分开,对吧?
但是,这是生产版本的问题,因为它有一个文件夹用于 static
和 templates
类型的文件,并且所有依赖项都是这样链接的。
如果您同时考虑static
和templates
,则将提供build
文件夹。
使用类似的东西
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")
你的烧瓶应用会运行良好。
【讨论】:
这个答案救了我。所有其他人都有一些不适合我的设置的警告。【参考方案4】:接受的答案对我不起作用。我用过
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
【讨论】:
【参考方案5】:我使用了一个只有一个 route / 的烧瓶服务器,它从 Create react app(CRA) 的构建文件夹中读取 index.html 文件
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()
以这种方式设置我遇到了错误,由于路径不匹配,静态文件无法正确提供,所以我使用的解决方案是
-
在 CRA 的 package.json 中添加主页属性并设置为 "/static"
"name":"应用名称", “版本”:””, “依赖项”: "homepage":"/static",....[其他键]
Add **homepage** key parallel to the **dependencies** key in the package.json file
这个 homepage 属性将在 CRA 的构建过程中使用,并用于代替 index.html 的 %PUBLIC_URL% 并附加到其他静态资产 URL 路径(您可以通过查看来验证构建过程后的 index.html 代码)
构建过程结束后,运行flask服务器,我们可以看到第一次带有/的GET请求,index.html将被服务,随后向 /static/static/js/[[filename]] 请求 HTML 文件中的其他静态资源,一切正常
【讨论】:
以上是关于使用 Flask 为使用 create-react-app 创建的前端提供服务的主要内容,如果未能解决你的问题,请参考以下文章
使用 Flask 为使用 create-react-app 创建的前端提供服务
Flask:资源解释为样式表,但使用 MIME 类型 text/html 传输
Gitlab Flask 页面:使用 .gitlab-ci.yml 文件为 Frozen Flask Application 安装 python GDAL 包时出现问题