Skip to content

fix(client): fallback to directory route when .html route does not exist - #1720

Open
MEMZ-CHROER wants to merge 2 commits into
vuepress:mainfrom
MEMZ-CHROER:fix/clean-urls-directory-route
Open

fix(client): fallback to directory route when .html route does not exist#1720
MEMZ-CHROER wants to merge 2 commits into
vuepress:mainfrom
MEMZ-CHROER:fix/clean-urls-directory-route

Conversation

@MEMZ-CHROER

Copy link
Copy Markdown

Summary

修复 clean URL(无后缀)访问目录页时被转成不存在的 .html 导致 404 / 无限重定向循环的问题。

问题

目录式内容(foo/README.md → 路由 /foo/)下,客户端访问 /foo(clean URL):

/foo  →  normalizeRoutePath → /foo.html(inferRoutePath 有意补 .html)
     →  routes 里没有 /foo.html(实际路由是 /foo/)
     →  redirects 里也没有 /foo.html
     →  返回 /foo.html → 404

部署在会规范化 URL 的静态托管(Vercel 等)时,托管层把 /foo.html 301 回目录 → 浏览器去斜杠 → 无限重定向循环

修复

packages/client/src/router/resolveRoutePath.ts:当规范化的 .html 路由不存在时,回退匹配目录路由/foo.html/foo/),前提是目录路由真实存在(避免误伤真正的 .html 页面)。

/foo  →  normalizeRoutePath → /foo.html(不存在)
     →  回退 → /foo/(存在)→ 命中 ✅

测试

新增 packages/client/tests/router/resolveRoutePath.spec.ts,覆盖:

  • 目录路由回退(/foo/foo/
  • 真实 .html 页面不受影响(/foo/bar.html/foo/bar.html
  • 404 路径保持原样(/not-exist/not-exist.html

验证

  • ✅ 全量 668 个测试通过
  • ✅ eslint 通过
  • ✅ oxfmt 格式通过

Closes #1719

When resolving a clean URL (no trailing slash) like /foo, the
normalizeRoutePath converts it to /foo.html. If the page is a
directory page (foo/README.md → /foo/), the /foo.html route does
not exist in the routes table, causing a 404 or an infinite
redirect loop when deployed behind static hosts that normalize
URLs (e.g. Vercel).

Fallback to the directory route /foo/ when the normalized .html
route does not exist, so that clean URLs can resolve to directory
pages correctly.

Fix #3239
@MEMZ-CHROER

Copy link
Copy Markdown
Author

本地验证记录(首次贡献者,CI 尚未放行,先在此附上)

已在本机完成全部 CONTRIBUTING.md 要求的检查:

检查 命令 结果
单元测试 vitest run ✅ 64 files / 668 tests 通过
Lint eslint ✅ 0 error
格式化 oxfmt --check ✅ 通过
提交规范 commitlint ✅ conventional commit

补丁逻辑resolveRoutePath):/foo → normalizeRoutePath → /foo.html(inferRoutePath 有意为之,有测试背书)→ routes 表里无 /foo.html回退 /foo/(存在) → 命中。仅当目录路由真实存在时才回退,不会误伤真正的 .html 页面。

新增测试覆盖:

  • 目录回退:/foo/foo/
  • 真实 .html 页面不受影响:/foo/bar.html/foo/bar.html
  • 404 保持原样:/not-exist/not-exist.html

关联 issue:#1719

@MEMZ-CHROER

Copy link
Copy Markdown
Author

#1538 的关系说明

已知 @Mister-Hope#1538feat: add route option)提供了更完整的 clean URL 支持(新增 route.cleanUrl,让 inferRoutePath 返回 clean 格式)。本 PR 与其不冲突

完全支持 #1538 的长远方向。

Refine the directory fallback logic to only trigger when the raw path
has no extension. The same-named file route (e.g. /foo.html from foo.md)
is prioritized over the directory route (/foo/), so /foo resolves to
/foo.html when foo.md exists, and only falls back to /foo/ when it does
not. This avoids conflating foo.md with foo/README.md.
@MEMZ-CHROER

Copy link
Copy Markdown
Author

更新:已按 @Mister-Hope 的反馈把回退逻辑收紧为安全回退,消除了"混淆 foo.mdfoo/README.md"的歧义:

改动resolveRoutePath):仅当原始路径无扩展名时才回退目录。同名文件路由优先:

/foo       → /foo.html(若 foo.md 存在)✅ 文件优先
            → /foo/(若仅 foo/README.md 存在)✅ 目录兜底
            → /foo.html → 404(两者都不存在)
/foo/bar.html → 原样(有扩展名,不触发回退)

测试新增:同名文件优先 + 纯目录兜底两个场景(669 个测试全过)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug report] clean URLs (无后缀访问目录页) 在客户端被转成不存在的 .html,导致 404 / 无限重定向循环

1 participant