Heroku 上 Flask API 与 Dash 应用的部署与集成

Heroku 上 Flask API 与 Dash 应用的部署与集成

本文探讨了在 Heroku 部署 Flask API 与 Dash 应用时常见的 405 Method Not Allowed 错误及其解决方案。核心问题在于 Heroku 的 Procfile 配置与 Flask 和 Dash 应用实例的交互方式。通过将 Dash 应用集成到主 Flask 实例中,并正确配置 Procfile 指向单一的 Flask 应用入口,可以有效解决路由冲突,实现 API 和 UI 的协同工作。教程将提供集成示例代码,并强调部署注意事项。

1. 背景与问题描述

在 heroku 平台部署 web 应用时,开发者常遇到将后端 api(如使用 flask 构建)与前端交互式界面(如使用 dash 构建)结合的需求。一个典型的场景是,flask api 负责数据接收和处理(例如,将远程数据写入 postgresql 数据库),而 dash 应用则提供数据可视化或管理界面。然而,当尝试在同一个 heroku 应用中同时运行这两个组件时,可能会遭遇 405 method not allowed 错误,尤其是在尝试向 flask api 端点发送 post 请求时。

原始问题中的错误信息 response content: b’a style=”color:#f60; text-decoration:underline;” title= “html”href=”https://www.php.cn/zt/15763.html” target=”_blank”>html>nn

405 Method Not Allowedn

Method Not Allowed

n

The method is not allowed for the requested URL.

n’ 明确指出服务器不接受对指定 URL 使用请求的方法(POST)。这通常不是认证问题,而是路由或服务器配置问题。

2. 深入理解问题根源:Flask、Dash 与 Heroku Procfile

问题的核心在于 Flask 和 Dash 应用实例的独立性以及 Heroku Procfile 的工作方式。

在提供的代码中,存在两个独立的应用程序实例:

app = Flask(__name__):这是一个标准的 Flask 应用实例,用于定义 API 路由(例如 /ingest)。dash_app = dash.Dash(__name__,):这是一个 Dash 应用实例,它内部也运行着一个 Flask 服务器 (dash_app.server)。

当在 Procfile 中定义 Heroku 的 Web 进程时,我们必须指定一个单一的入口点供 Gunicorn(Heroku 推荐的 WSGI HTTP 服务器)启动。

如果 Procfile 指向 your_module_name:app,Gunicorn 将启动 app = Flask(__name__) 实例。此时,@app.route 定义的 API 路由将可用,但 dash_app 及其路由将无法访问。如果 Procfile 指向 your_module_name:server(这里的 server 通常指 dash_app.server),Gunicorn 将启动 Dash 应用内部的 Flask 实例。此时,Dash 界面将可用,但 app = Flask(__name__) 实例上定义的 API 路由将无法访问,从而导致 405 Method Not Allowed 错误,因为 /ingest 路径对于 Dash 应用的内部 Flask 实例而言可能不存在或不允许 POST 方法。

简而言之,尝试在同一个 Heroku dyno 中通过单一 Procfile 入口同时运行两个独立的 Flask/Dash 实例是行不通的。

3. 解决方案:将 Dash 应用集成到现有 Flask 应用中

最推荐且最健壮的解决方案是将 Dash 应用作为子应用集成到主 Flask 应用中。这样,所有路由(无论是 Flask API 路由还是 Dash UI 路由)都将由同一个 Flask 服务器实例处理。

3.1 修正后的应用代码

以下是整合了 Flask API 和 Dash UI 的 Python 应用代码示例:

from flask import Flask, request, jsonify, make_responsefrom flask_cors import CORSimport dashfrom dash import dcc, html, Input, Outputimport jsonimport 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)

3.2 客户端请求脚本

客户端请求脚本保持不变,因为它只需知道 API 端点。

import requestsdata = {    "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' # 客户端的原始 tokenheaders = {'Authorization': f'Bearer {token}'} # 按照约定发送 Bearer tokenresponse = 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()}') # 解码以便阅读

3.3 Heroku Procfile 配置

由于我们将 Dash 集成到了主 Flask 应用 app 中,现在只需要 Procfile 指向这个主 Flask 应用实例。假设你的 Python 文件名为 app.py:

web: gunicorn app:app

这里的 app:app 表示:

app (第一个):指的是你的 Python 模块文件名(例如 app.py)。app (第二个):指的是该模块中 Gunicorn 应该启动的 Flask 应用程序实例的变量名 (app = Flask(__name__))。

4. 部署注意事项

依赖管理: 确保 requirements.txt 文件中包含了所有必要的库,例如 flask, dash, dash-core-components, dash-html-components, flask-cors, gunicorn, psycopg2 (如果使用 PostgreSQL)。环境变量: 对于敏感信息(如数据库连接字符串、API 密钥),应使用 Heroku 环境变量而非硬编码。例如,Heroku 会自动为 Heroku Postgres 数据库提供 DATABASE_URL 环境变量。CORS 配置: 在生产环境中,Access-Control-Allow-Origin: ‘*’ 应该被替换为你的前端应用或客户端的特定域名,以增强安全性。授权令牌: 确保客户端发送的授权令牌格式(例如 Bearer )与服务器端验证的格式一致。Gunicorn 配置: 如果需要更高级的 Gunicorn 配置(例如工作进程数量、超时时间),可以在 Procfile 中添加参数或创建 gunicorn.conf.py 文件。数据库连接: 在 Heroku 上连接 PostgreSQL 数据库时,通常需要 psycopg2-binary 库,并且连接字符串(DATABASE_URL)会自动注入到环境中。

5. 总结

通过将 Dash 应用作为子应用集成到主 Flask 应用中,并确保 Procfile 正确指向这个统一的 Flask 实例,我们能够成功地在 Heroku 上部署一个同时提供 API 服务和交互式 UI 的应用。这种方法避免了多个应用实例之间的冲突,简化了部署和管理,并解决了 405 Method Not Allowed 这一常见的部署问题。理解 Heroku Procfile 与应用实例的对应关系是成功部署此类复杂应用的关键。

以上就是Heroku 上 Flask API 与 Dash 应用的部署与集成的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1373663.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月14日 13:27:36
下一篇 2025年12月14日 13:27:47

相关推荐

  • Python数值计算陷阱:正确处理用户输入的成绩数据

    本文深入探讨Python中用户输入数据导致数值计算错误的常见陷阱。当用户输入数字时,Python默认将其视为字符串,若直接进行算术运算,可能发生字符串连接而非数值相加。本教程将详细解析此问题,并提供两种将字符串输入正确转换为整数的有效方法,确保数据处理的准确性与程序的健壮性。 在python编程中,…

    2025年12月14日
    000
  • HDF5 大数据存储优化:高效分块策略与实践

    处理大型科学数据集时,HDF5 是一种常用的存储方案,但其写入性能往往成为瓶颈。本文旨在探讨如何通过优化 HDF5 的分块(chunking)策略来显著提升大数据集的写入效率。我们将深入分析不当分块导致性能低下的原因,并提供一个与数据访问模式高度匹配的优化方案,辅以 Python 示例代码,帮助读者…

    2025年12月14日
    000
  • Django中实现可选ForeignKey字段的表单验证指南

    本文详细探讨了在Django应用中,即使模型层已将ForeignKey字段设置为可选(blank=True, null=True),在自定义表单中仍可能被强制要求填写的问题。核心解决方案是在自定义的forms.ModelChoiceField中明确设置required=False,以确保表单验证与模…

    2025年12月14日
    000
  • SQLAlchemy动态WHERE子句构建指南

    本文旨在指导读者如何在SQLAlchemy中构建动态的WHERE子句,以适应不同客户端输入和多变的查询需求。通过将过滤条件抽象为可迭代的表达式列表,并利用辅助函数进行应用,我们能够灵活地组合查询条件,从而实现高度可配置的数据检索功能,有效应对简单键值对或复杂逻辑组合的动态过滤场景。 1. 引言:动态…

    2025年12月14日
    000
  • SQLAlchemy动态查询:灵活构建WHERE条件

    本文旨在探讨如何在SQLAlchemy中实现动态的WHERE子句,以应对客户端输入或业务逻辑变化带来的查询条件不确定性。我们将介绍一种核心策略:将查询条件预定义为独立的表达式列表,并通过迭代方式将其应用到SELECT语句中,从而实现高度灵活且可扩展的查询构建。此外,文章还将涵盖如何将字典形式的动态输…

    2025年12月14日
    000
  • Pandas整数类型默认行为与测试断言策略

    本文探讨了在64位Python环境中,Pandas Series在显式指定dtype=int时可能默认使用int32而非int64的问题,及其对DataFrame测试中严格类型检查的影响。文章提出了一种自定义的assert_frame_equiv函数作为解决方案,通过在比较前统一等效数据类型,实现了…

    2025年12月14日
    000
  • HDF5 大数据分块存储性能优化指南

    本文旨在解决使用 h5py 向 HDF5 文件写入大型分块数据集时遇到的性能瓶颈。通过分析不合理的分块策略和索引方式,我们提出了一种优化的分块大小和数据写入方法,显著提升了写入效率。文章详细介绍了如何根据数据访问模式选择合适的块形状和大小,并提供了具体的 Python 代码示例和最佳实践,帮助开发者…

    2025年12月14日
    000
  • 使用Python Pandas通过字典实现DataFrame列的模糊分类

    本文将详细介绍如何利用Python Pandas库,结合字典和apply函数,为DataFrame添加基于子字符串匹配的分类列。当DataFrame的原始数据项并非字典键的精确匹配,而是包含字典键作为子字符串时,传统的map方法会失效。本教程将提供一种高效且灵活的解决方案,通过自定义匹配逻辑实现动态…

    2025年12月14日
    000
  • 解决Python pip安装失败:系统环境变量PATH配置指南

    当Python的pip工具在安装新包时出现“Fatal error in launcher”错误,通常是由于系统环境变量中Python路径配置不正确所致。本文将详细指导您如何修改系统环境变量PATH,确保pip能正确找到Python解释器及其脚本,从而顺利安装和管理Python包。 理解“Fatal…

    2025年12月14日
    000
  • Odoo 15 配送单地址显示错误的根源与排查指南

    本文深入探讨Odoo 15配送单错误显示客户地址而非实际送货地址的问题。通过分析报告模板report_deliveryslip.xml和res.partner模型的commercial_partner_id字段计算逻辑,揭示了根源在于送货地址伙伴记录的类型及其父子关系。文章提供了详细的排查步骤和代码…

    2025年12月14日
    000
  • Python中利用函数生成斐波那契数列的迭代实现指南

    本文详细介绍了如何在Python中利用迭代方法和自定义函数生成斐波那契数列。通过一个具体的函数定义和调用示例,文章阐明了斐波那契数列的生成逻辑,并着重强调了函数定义后必须显式调用的关键步骤。教程还涵盖了代码实现细节、常见问题及最佳实践,旨在帮助初学者掌握函数式编程在序列生成中的应用。 斐波那契数列简…

    2025年12月14日
    000
  • Python函数实现斐波那契数列生成与调用教程

    本教程详细讲解如何使用Python函数通过循环生成斐波那契数列。文章从函数定义、数列生成逻辑入手,重点阐述了函数调用这一关键步骤,并提供了代码示例。此外,还介绍了如何优化函数设计,使其返回结果而非直接打印,并讨论了边界条件处理和输入验证等实用注意事项,旨在帮助初学者掌握高效、健壮的斐波那契数列实现方…

    2025年12月14日
    000
  • Python泛型编程:深入理解TypeVar与Union类型在约束中的兼容性问题

    本文深入探讨了Python中TypeVar与Union类型在泛型约束中常见的兼容性问题。当TypeVar被显式约束为一组特定类型时,它不会自动接受这些类型的联合体。文章提供了两种主要解决方案:一是将联合类型明确添加到TypeVar的约束集中,二是使用bound参数来定义类型变量的上限,从而实现更灵活…

    2025年12月14日
    000
  • 动态生成Plotly与Matplotlib兼容的离散RGB颜色列表

    本文旨在解决在Plotly和Matplotlib绘图中,当数据分组数量超出Plotly内置调色板限制(如24种)时,如何动态生成足够数量且格式为RGB的离散颜色方案。针对Matplotlib仅支持RGB格式颜色的需求,文章提出了一种基于随机生成并确保颜色唯一性的Python实现方法,以克服手动拼接调…

    2025年12月14日
    000
  • Python中高效生成斐波那契数列:函数定义与调用实践

    本文详细讲解如何在Python中利用函数高效生成斐波那契数列。通过一个循环实现的示例函数,读者将学习数列的生成逻辑,并特别强调函数定义后必须显式调用才能执行其内部逻辑并获得预期输出,避免初学者常犯的遗漏调用错误。 斐波那契数列是一个经典的数学序列,广泛应用于计算机科学、生物学等领域。该数列的特点是,…

    2025年12月14日
    000
  • HDF5大型数据集分块存储与写入性能优化

    本文深入探讨了使用H5py库处理大型复杂数据集时,通过优化HDF5分块存储策略和数据写入方式来解决写入效率低下的问题。核心内容包括分析不当分块大小和形状对性能的影响,并提出将分块尺寸与数据访问模式对齐、采用精确索引写入数据等优化方案,显著提升了大型矩阵数据集的创建速度。 HDF5分块存储与大型数据集…

    2025年12月14日
    000
  • Python 类型提示:理解 TypeVar 约束与联合类型

    在 Python 类型提示中,TypeVar 与联合类型(Union Type)的交互常令人困惑。本文将深入探讨当一个 TypeVar 被约束为特定类型时,为何它不能直接接受一个包含这些类型的联合类型,并提供两种有效的解决方案:通过扩展 TypeVar 的约束列表来包含联合类型本身,或使用 boun…

    2025年12月14日
    000
  • Python数值计算中的常见陷阱:input()与类型转换深度解析

    本文深入探讨了Python中因input()函数默认返回字符串而导致的数值计算错误,特别是当尝试计算平均值时发生的字符串拼接问题。通过详细的代码示例,文章阐述了如何通过显式类型转换(如int()或float())来确保正确的数学运算,并提供了两种有效的解决方案,旨在帮助开发者避免此类常见陷阱,编写出…

    2025年12月14日
    000
  • Python数值计算陷阱:深入理解input()与类型转换

    本文深入探讨了Python中input()函数返回字符串类型导致数值计算错误这一常见问题。通过分析字符串拼接与数值相加的区别,提供了两种核心解决方案:在计算时进行类型转换,或在获取输入时即时转换类型。文章还强调了类型转换的重要性、潜在的ValueError处理,并给出了结构更优化的代码示例,旨在帮助…

    2025年12月14日
    000
  • Odoo 15 送货单地址显示错误排查与解决方案

    本文深入探讨了Odoo 15企业版中送货单地址显示错误的复杂问题。当送货单PDF偶尔错误地显示客户主地址而非实际送货地址时,通常是由于QWeb报告模板中对commercial_partner_id的判断逻辑与res.partner记录类型(公司或个人)配置不符所致。文章通过详细的排查步骤、代码分析和…

    2025年12月14日
    000

发表回复

登录后才能评论
关注微信