Airflow 2.3.3插件托管Great Expectations HTML页面404问题求助
解决Airflow插件中Great Expectations HTML链接404问题
问题
Airflow 2.3.3插件用于托管Great Expectations 0.1.5生成的HTML输出,目前能成功加载index.html,但点击页面内的任意链接(如validations/src/dag1/warning/...路径下的HTML文件)时,会跳转到Airflow 404页面。
现有插件代码
from airflow.plugins_manager import AirflowPlugin from flask import Blueprint from flask_appbuilder import expose, BaseView as AppBuilderBaseView import os class GreatExpectationsView(AppBuilderBaseView): @expose('/') def list(self): return self.render_template("index.html", content="") v_appbuilder_view = GreatExpectationsView() v_appbuilder_package = { "name": "Great Expectations", "category": "Browse", "view": v_appbuilder_view, } bp = Blueprint( "great_expectation", __name__, template_folder="/opt/great_expectations", static_folder='/opt/great_expectations/static', static_url_path='/greatexpectationsview/static' ) class AirflowGEPlugin(AirflowPlugin): name = "great_expectation" appbuilder_views = [v_appbuilder_package] flask_blueprints = [bp]
Great Expectations HTML文件夹结构
/opt/great_expectations/ |-- expectations | `-- src | `-- dag1 | `-- warning.html |-- index.html |-- static | |-- fonts | | `-- HKGrotesk | | |-- HKGrotesk-Bold.otf | | |-- HKGrotesk-BoldItalic.otf | | |-- HKGrotesk-Italic.otf | | |-- HKGrotesk-Light.otf | | |-- HKGrotesk-LightItalic.otf | | |-- HKGrotesk-Medium.otf | | |-- HKGrotesk-MediumItalic.otf | | |-- HKGrotesk-Regular.otf | | |-- HKGrotesk-SemiBold.otf | | `-- HKGrotesk-SemiBoldItalic.otf | |-- images | | |-- favicon.ico | | |-- glossary_scroller.gif | | |-- iterative-dev-loop.png | | |-- logo-long-vector.svg | | |-- logo-long.png | | |-- short-logo-vector.svg | | |-- short-logo.png | | `-- validation_failed_unexpected_values.gif | `-- styles | |-- data_docs_custom_styles_template.css | `-- data_docs_default_styles.css `-- validations `-- src `-- dag1 `-- warning |-- 20220914-170251-src_dag1 | `-- 20220914T170251.386427Z | `-- 1811b6d2fab709c0e35cab11873288dc.html `-- 20220914-182123-src_dag1 `-- 20220914T182123.138118Z `-- 1811b6d2fab709c0e35cab11873288dc.html
解决方案
问题核心是现有插件仅处理了根路径的请求,未对GE生成的其他HTML文件路径提供路由支持。修改插件代码,添加动态路由捕获所有文件请求,并使用send_from_directory返回对应文件:
from airflow.plugins_manager import AirflowPlugin from flask import Blueprint, send_from_directory from flask_appbuilder import expose, BaseView as AppBuilderBaseView import os # 定义GE的根目录 GE_ROOT_DIR = "/opt/great_expectations" class GreatExpectationsView(AppBuilderBaseView): @expose('/') def list(self): # 根路径返回index.html return send_from_directory(GE_ROOT_DIR, "index.html") @expose('/<path:filename>') def serve_file(self, filename): # 捕获所有子路径请求,返回对应文件 return send_from_directory(GE_ROOT_DIR, filename) v_appbuilder_view = GreatExpectationsView() v_appbuilder_package = { "name": "Great Expectations", "category": "Browse", "view": v_appbuilder_view, } bp = Blueprint( "great_expectation", __name__, static_folder='/opt/great_expectations/static', static_url_path='/greatexpectationsview/static' ) class AirflowGEPlugin(AirflowPlugin): name = "great_expectation" appbuilder_views = [v_appbuilder_package] flask_blueprints = [bp]
关键修改说明
- 添加动态路由:新增
@expose('/<path:filename>')装饰的serve_file方法,匹配所有子路径请求。 - 改用send_from_directory:替换
render_template为send_from_directory,直接返回GE目录下的静态HTML文件(GE生成的HTML不是Jinja模板,无需渲染)。 - 移除template_folder:既然不再使用模板渲染,Blueprint中可以去掉
template_folder配置。 - 定义GE_ROOT_DIR常量:统一管理GE的根目录路径,便于维护。
额外注意事项
- 确保Airflow运行进程对
/opt/great_expectations目录有读取权限,否则会返回权限错误。 - 静态资源(CSS、图片、字体)已通过Blueprint的
static_folder和static_url_path配置处理,无需额外修改。
内容的提问来源于stack exchange,提问作者wymangr
相关产品推荐
相关产品推荐

