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; } }该类的行为可以拆解为三条规则:
- 默认自动探测:当
BASE_URL为默认值/时,GetBaseUrl()会根据HTTPS/HTTP_X_FORWARDED_PROTO请求头与HTTP_HOST自动拼出当前访问的协议与主机(见 helpers/UrlManager.php),例如https://grocy.example.com; - 显式配置优先:当
BASE_URL配置为子目录地址(如https://example.com/grocy)时,直接使用该值,不再自动探测; - URL 重写开关:当
DISABLE_URL_REWRITING为true且目标不是静态资源时,会在路径中插入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 不做额外前缀匹配这是最简单的方式。UrlManager在BASE_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):
- 覆盖文件:
data/settingoverrides/BASE_URL.txt,文件内容即配置值(README 中描述,见 README.md); - 环境变量:
GROCY_BASE_URL; - 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_URL与BASE_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),仅供参考