grocy 1.8.2 修复实录:让登录表单尊重 BASE_URL 配置与子目录部署的 URL 生成机制
2026/9/16 19:48:21 网站建设 项目流程

grocy 1.8.2 修复实录:让登录表单尊重 BASE_URL 配置与子目录部署的 URL 生成机制

【免费下载链接】grocyERP beyond your fridge - Grocy is a web-based self-hosted groceries & household management solution for your home项目地址: https://gitcode.com/GitHub_Trending/gr/grocy

在 grocy 1.8.1 引入BASE_URL配置项以支持子目录部署之后,1.8.2(2018-04-18)迅速修复了一个直接影响用户体验的问题:登录表单没有遵守配置的BASE_URL(见 changelog/17_1.8.2_2018-04-18.md)。本文以这一修复为切入点,结合当前仓库源码,完整讲解BASE_URL/BASE_PATH配置的语义、UrlManager的 URL 构造机制、登录页与登录流程中的 URL 生成链路,以及如何正确配置使 grocy 运行在站点根目录或子目录下。读完本文,你将能独立排查"登录后跳转错误""登录表单提交 404""资源路径丢失"等子目录部署常见问题。

一、修复背景:1.8.1 引入 BASE_URL,1.8.2 补齐登录链路

该修复并非孤立事件,而是对 1.8.1 新功能的完善。1.8.1 的更新日志明确记录(见 changelog/16_1.8.1_2018-04-18.md):

  • 新增配置项BASE_URL,用于定义安装的基础 URL,目标是让子目录安装成为可能(对应 issue #3);
  • 补充了部分缺失的翻译。

也就是说:1.8.1 把"告诉 grocy 它部署在哪个基础 URL 上"的能力加了进来,但当时登录页面的 URL 生成逻辑尚未接入这一配置。1.8.2 正是把这块"漏网之鱼"补上——修复登录表单未遵守已配置的BASE_URL

判断依据:当前仓库中的 config-dist.php 仍保留着对BASE_URL的完整注释说明,而 changelog/16_1.8.1_2018-04-18.md 与 changelog/17_1.8.2_2018-04-18.md 成对记录了这两个版本在该功能上的递进关系。

二、BASE_URL 与 BASE_PATH:两个极易混淆的部署配置

要理解这次修复,首先必须分清 grocy 提供的两个相关配置项。两者都定义在 config-dist.php 中,但职责不同:

BASE_URL:完整的基础 URL

// The base URL of your installation, // should be just "/" when running directly under the root of a (sub)domain // or for example "https://example.com/grocy" when using a subdirectory Setting('BASE_URL', '/');
  • 默认值/,表示运行在(子)域名的根路径下;
  • 子目录场景:例如部署在https://example.com/grocy时,应设置为https://example.com/grocy
  • 作用范围:它被注入到UrlManager(见下文),用于生成页面跳转、表单 action、资源引用等全部 URL。

BASE_PATH:Slim 框架的 base path

// When running Grocy in a subdirectory, this should be set to the relative path, otherwise empty // It needs to be set to the part (of the URL) AFTER the document root, // if URL rewriting is disabled, including index.php // Example with URL Rewriting support: // Root URL = https://example.com/grocy // => BASE_PATH = /grocy // Example without URL Rewriting support: // Root URL = https://example.com/grocy/public/index.php/ // => BASE_PATH = /grocy/public/index.php Setting('BASE_PATH', '');
  • 默认值:空字符串;
  • 作用范围:Slim 应用的路由匹配。在 app.php 中:
// Set base path if defined if (!empty(GROCY_BASE_PATH)) { $app->setBasePath(GROCY_BASE_PATH); }

BASE_PATH是文档根目录之后的那段 URL 路径(不包含协议与域名),它决定 Slim 如何匹配路由;BASE_URL则是完整的、用于对外生成链接的 URL。两者配合使用才能让子目录部署完整生效。

补充说明:BASE_PATH是后续版本(2.6.1,见 changelog/56_2.6.1_2020-03-06.md)中明确要求子目录用户必须设置的新配置;1.8.x 时代主要是通过BASE_URL解决链接生成问题。

三、核心机制:UrlManager 如何构造 URL

1.8.2 修复的实质,就是让登录表单的action走通UrlManager这条统一 URL 生成链路。当前仓库中的实现位于 helpers/UrlManager.php:

public function __construct(string $basePath) { if ($basePath === '/') { $this->BasePath = $this->GetBaseUrl(); } else { $this->BasePath = $basePath; } } public function ConstructUrl($relativePath, $isResource = false) { if (GROCY_DISABLE_URL_REWRITING === false || $isResource === true) { return rtrim($this->BasePath, '/') . $relativePath; } else // Is not a resource and URL rewriting is disabled { return rtrim($this->BasePath, '/') . '/index.php' . $relativePath; } }

该类的行为可以拆解为三条规则:

  1. 默认自动探测:当BASE_URL为默认值/时,GetBaseUrl()会根据HTTPS/HTTP_X_FORWARDED_PROTO请求头与HTTP_HOST自动拼出当前访问的协议与主机(见 helpers/UrlManager.php),例如https://grocy.example.com
  2. 显式配置优先:当BASE_URL配置为子目录地址(如https://example.com/grocy)时,直接使用该值,不再自动探测;
  3. URL 重写开关:当DISABLE_URL_REWRITINGtrue且目标不是静态资源时,会在路径中插入index.php(对应DISABLE_URL_REWRITING配置项,见 config-dist.php)。

UrlManager在应用初始化时以单例方式注册进容器(app.php),并在多个地方被消费:

  • 数据库迁移完成后,跳转到根路由:header('Location: ' . (new UrlManager(GROCY_BASE_URL))->ConstructUrl('/'))(app.php);
  • 日历服务构造 iCal 订阅链接(services/CalendarService.php、controllers/Api/CalendarApiController.php);
  • OpenAPI 文档与 API Key 管理页面的链接生成(controllers/Api/OpenApiController.php)。

四、修复点还原:登录表单的 URL 生成链路

从当前仓库源码可以完整还原登录页面的 URL 生成路径,这正是 1.8.2 修复所覆盖的代码链路。

4.1 路由注册

登录相关路由在 routes.php 中注册:

// Login routes $group->get('/login', [LoginController::class, 'LoginPage'])->setName('login'); $group->post('/login', [LoginController::class, 'ProcessLogin'])->setName('login'); $group->get('/logout', [LoginController::class, 'Logout']);

4.2 视图中的表单 action

登录表单模板 views/login.blade.php 通过$U()辅助函数生成提交地址:

<form method="post" action="{{ $U('/login') }}" id="login-form" novalidate>

$U()在 controllers/BaseController.php 中定义,本质上是容器内UrlManager的封装:

$this->View->set('U', function ($relativePath, $isResource = false) use ($container) { return $container->get('UrlManager')->ConstructUrl($relativePath, $isResource); });

在 1.8.2 修复之前,登录表单的 action 若使用硬编码或未经过UrlManager的路径,在子目录部署时就会生成错误地址,导致登录表单提交后 404 或跳转到错误位置;修复后,action="{{ $U('/login') }}"生成的地址会正确带上BASE_URL前缀。

4.3 登录处理与跳转

表单以 POST 提交到/login后,由 controllers/LoginController.php 的ProcessLogin处理:

if ($authMiddlewareClass::ProcessLogin($postParams)) { return $response->withRedirect($this->AppContainer->get('UrlManager')->ConstructUrl('/')); } else { return $response->withRedirect($this->AppContainer->get('UrlManager')->ConstructUrl('/login?invalid=true')); }

成功登录跳转到ConstructUrl('/')(即BASE_URL对应的首页),失败则带invalid=true参数回到登录页——两处跳转同样走UrlManager。登出时也使用ConstructUrl('/')完成重定向(controllers/LoginController.php)。

值得注意的实现细节:登录表单的密码字段名为password_base64(views/login.blade.php),ProcessLogin会在处理前将其base64_decode还原为明文密码(controllers/LoginController.php),避免密码以明文形式直接随表单传输。

五、配置实践:根目录与子目录两种部署方式

综合以上机制,可得到两种典型配置(修改data/config.php,由config-dist.php复制而来):

场景一:运行在域名根路径(默认)

Setting('BASE_URL', '/'); // 默认值,自动探测协议与主机 Setting('BASE_PATH', ''); // 默认值,Slim 不做额外前缀匹配

这是最简单的方式。UrlManagerBASE_URL === '/'时自动根据HTTPS/HTTP_HOST构造完整基础地址。

场景二:运行在子目录(如 https://example.com/grocy)

Setting('BASE_URL', 'https://example.com/grocy'); // 完整基础 URL Setting('BASE_PATH', '/grocy'); // 文档根之后的部分

BASE_URL保证登录表单 action、跳转链接、资源引用都带/grocy前缀;BASE_PATH保证 Slim 路由在/grocy前缀下仍能正确匹配。若同时禁用了 URL 重写(DISABLE_URL_REWRITING => true),BASE_PATH还需包含index.php(参见 config-dist.php 的注释示例)。

配置的三种覆盖优先级

无论采用哪种场景,BASE_URL都可以通过三种途径设置,优先级从高到低(见 config-dist.php):

  1. 覆盖文件data/settingoverrides/BASE_URL.txt,文件内容即配置值(README 中描述,见 README.md);
  2. 环境变量GROCY_BASE_URL
  3. config.php 中的默认值Setting('BASE_URL', ...)

验证配置是否生效

修改BASE_URL(或BASE_PATH)后,grocy 会通过版本/配置哈希自动清空视图缓存并触发必要的迁移。在 app.php 中:

// Empty data/viewcache when and trigger database migrations when: // The version changed (so when an update was done) // GROCY_BASE_URL OR GROCY_BASE_PATH changed $hash = hash('sha256', file_get_contents(__DIR__ . '/version.json') . GROCY_BASE_URL . GROCY_BASE_PATH);

随后应用会重定向到ConstructUrl('/')完成初始化。你可以直接观察:

  • 打开登录页后,浏览器地址栏与表单action是否带上了预期前缀;
  • 登录成功后跳转的首页地址是否正确;
  • 页面中的静态资源(CSS/JS,即$U(path, true)的资源分支)是否可加载。

六、小结

grocy 1.8.2 的这行修复日志虽然简短,背后却是一条完整的 URL 生成链路:BASE_URL配置 →UrlManager::ConstructUrl()→ 视图辅助函数$U()→ 登录表单 action 与登录/登出重定向。理解这条链路,不仅能解释 1.8.2 修了什么,更能让你在子目录部署、反向代理、禁用 URL 重写等场景下准确配置BASE_URLBASE_PATH,并快速定位"登录跳转错乱"类问题的根因。

相关文件索引

  • changelog/17_1.8.2_2018-04-18.md:本次修复的原始变更记录
  • changelog/16_1.8.1_2018-04-18.md:BASE_URL 配置项引入记录
  • config-dist.php:BASE_PATH / BASE_URL 完整注释与示例
  • helpers/UrlManager.php:URL 构造核心实现
  • views/login.blade.php:登录表单 action 的 URL 生成
  • controllers/LoginController.php:登录处理与跳转逻辑
  • app.php:配置哈希、视图缓存与 BASE_PATH 注入
  • routes.php:登录路由注册

【免费下载链接】grocyERP beyond your fridge - Grocy is a web-based self-hosted groceries & household management solution for your home项目地址: https://gitcode.com/GitHub_Trending/gr/grocy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询