
在 heroku 平台部署 web 应用时,开发者常遇到将后端 api(如使用 flask 构建)与前端交互式界面(如使用 dash 构建)结合的需求。一个典型的场景是,flask api 负责数据接收和处理(例如,将远程数据写入 postgresql 数据库),而 dash 应用则提供数据可视化或管理界面。然而,当尝试在同一个 heroku 应用中同时运行这两个组件时,可能会遭遇 405 method not allowed 错误,尤其是在尝试向 flask api 端点发送 post 请求时。
原始问题中的错误信息 response content: b'<!doctype html>\n<html lang=en>\n<title>405 Method Not Allowed</title>\n<h1>Method Not Allowed</h1>\n<p>The method is not allowed for the requested URL.</p>\n' 明确指出服务器不接受对指定 URL 使用请求的方法(POST)。这通常不是认证问题,而是路由或服务器配置问题。
问题的核心在于 Flask 和 Dash 应用实例的独立性以及 Heroku Procfile 的工作方式。
在提供的代码中,存在两个独立的应用程序实例:
当在 Procfile 中定义 Heroku 的 Web 进程时,我们必须指定一个单一的入口点供 Gunicorn(Heroku 推荐的 WSGI HTTP 服务器)启动。
简而言之,尝试在同一个 Heroku dyno 中通过单一 Procfile 入口同时运行两个独立的 Flask/Dash 实例是行不通的。
最推荐且最健壮的解决方案是将 Dash 应用作为子应用集成到主 Flask 应用中。这样,所有路由(无论是 Flask API 路由还是 Dash UI 路由)都将由同一个 Flask 服务器实例处理。
以下是整合了 Flask API 和 Dash UI 的 Python 应用代码示例:
from flask import Flask, request, jsonify, make_response
from flask_cors import CORS
import dash
from dash import dcc, html, Input, Output
import json
import os # 用于获取数据库连接字符串
# 1. 创建主 Flask 应用实例
app = Flask(__name__)
CORS(app) # 为主 Flask 应用启用 CORS
# 2. 将 Dash 应用集成到现有的 Flask 应用中
# 通过 server=app 参数,Dash 会使用我们已经创建的 Flask 应用实例
# url_base_pathname 可以指定 Dash 应用的根路径,例如 /dashboard/
dash_app = dash.Dash(__name__, server=app, url_base_pathname='/dashboard/')
# 3. 定义 Flask API 路由
# 这个路由现在属于主 Flask 应用
@app.route('/ingest', methods=['OPTIONS', 'POST'])
def handle_ingest():
# 处理 CORS 预检请求
if request.method == 'OPTIONS':
response = make_response()
response.headers.add('Access-Control-Allow-Origin', '*') # 生产环境请指定具体域名
response.headers.add('Access-Control-Allow-Headers', 'Authorization, Content-Type')
response.headers.add('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
return response
# 认证逻辑
token = request.headers.get('Authorization')
# 客户端发送的是 'Bearer too_many_secrets',所以这里也要匹配
valid_tokens = ["Bearer too_many_secrets"]
if token in valid_tokens:
data = request.json # 假设数据以 JSON 格式发送
# --- 在这里执行数据验证和写入 PostgreSQL 数据库的逻辑 ---
# 示例:连接到 Heroku Postgres 数据库并插入数据
# import psycopg2
# DATABASE_URL = os.environ.get('DATABASE_URL') # Heroku 会自动提供
# try:
# conn = psycopg2.connect(DATABASE_URL, sslmode='require')
# cur = conn.cursor()
# # 示例:创建一个表并插入数据
# # cur.execute("CREATE TABLE IF NOT EXISTS sensor_data (id SERIAL PRIMARY KEY, sensor TEXT, value REAL, timestamp TIMESTAMPTZ DEFAULT NOW());")
# # cur.execute("INSERT INTO sensor_data (sensor, value) VALUES (%s, %s);", (data.get('sensor'), data.get('value')))
# conn.commit()
# cur.close()
# conn.close()
# print(f"Data ingested successfully: {data}")
# return jsonify({"message": "Data ingested successfully", "received_data": data}), 200
# except Exception as e:
# print(f"Database error: {e}")
# return jsonify({"message": "Failed to ingest data due to database error"}), 500
# 仅为演示,实际应写入数据库
print(f"Success: Data ingested successfully: {data}")
return jsonify({"message": "Data ingested successfully", "received_data": data}), 200
else:
print("Unauthorized user: Your token was Invalid")
return jsonify({"message": "Unauthorized"}), 401
# 4. 定义 Dash 应用的布局和回调
# Dash 应用现在是主 Flask 应用的一个部分
dash_app.layout = html.Div(children=[
html.H1(children='Heroku 集成应用'),
html.P('欢迎来到 Dash 仪表板!'),
dcc.Link('访问数据摄取 API 端点', href='/ingest', refresh=True), # 链接到 Flask API
html.Div(id='output-message', style={'margin-top': '20px'})
])
# 示例 Dash 回调 (如果需要)
# @dash_app.callback(
# Output('output-message', 'children'),
# Input('url', 'pathname') # 需要 dcc.Location 组件才能获取 pathname
# )
# def display_page(pathname):
# if pathname == '/dashboard/':
# return html.Div("您正在查看 Dash 仪表板首页。")
# elif pathname == '/ingest':
# return html.Div("您已点击了 API 端点链接,但此页面本身不提供交互。")
# return html.Div("未知页面")
# 5. 主程序入口
if __name__ == '__main__':
# 在本地运行,Flask 应用将作为主服务器
app.run(debug=True)客户端请求脚本保持不变,因为它只需知道 API 端点。
import requests
data = {
"sensor": "temperature",
"value": 25.5
}
# 假设 Heroku 应用的 URL 是 'https://my_app.herokuapp.com/'
# API 端点现在是 '/ingest'
api_endpoint = 'https://my_app.herokuapp.com/ingest'
token = 'too_many_secrets' # 客户端的原始 token
headers = {'Authorization': f'Bearer {token}'} # 按照约定发送 Bearer token
response = requests.post(api_endpoint, json=data, headers=headers, verify=True)
if response.status_code == 200:
print("Data sent successfully")
print(f"Response: {response.json()}")
else:
print(f"Failed to send data. Status code: {response.status_code}")
print(f'Response content: {response.content.decode()}') # 解码以便阅读由于我们将 Dash 集成到了主 Flask 应用 app 中,现在只需要 Procfile 指向这个主 Flask 应用实例。 假设你的 Python 文件名为 app.py:
web: gunicorn app:app
这里的 app:app 表示:
通过将 Dash 应用作为子应用集成到主 Flask 应用中,并确保 Procfile 正确指向这个统一的 Flask 实例,我们能够成功地在 Heroku 上部署一个同时提供 API 服务和交互式 UI 的应用。这种方法避免了多个应用实例之间的冲突,简化了部署和管理,并解决了 405 Method Not Allowed 这一常见的部署问题。理解 Heroku Procfile 与应用实例的对应关系是成功部署此类复杂应用的关键。
以上就是Heroku 上 Flask API 与 Dash 应用的部署与集成的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号