url統一定義

This commit is contained in:
owenchen committed 2026-10-22 09:09:13 +08:00
1 parent 69046e827c
commit c6eb00b406
9 files changed
+228 -65

No files matched your search

+26 -10
View File
@@ -8,7 +8,7 @@
## 1. 一句话结论
后端下发的文件地址有**三种形态**,端上不能直接塞进 `<image src>` / 播放器,必须统一过 `absoluteFileUrl()`;加载失败还要给一次公网回落 `wanFileUrl()`。
后端下发的文件地址有**三种形态**,端上不能直接塞进 `<image src>` / 播放器,必须统一过 `absoluteFileUrl()`;加载失败还要给一次公网回落 `wanFileUrl()`。归一规则已收敛为**单一入口** `utils/fileUrl.ts`,并由 `common/http.interceptor.ts` 在响应层调 `normalizePayloadFileUrls` 全局洗掉错主机绝对直链(详见第 4 节)。
## 2. 后端地址的三种形态
@@ -40,23 +40,39 @@
```
stripLoopbackHost(url) 把 localhost / 127.0.0.1 / 0.0.0.0 剥成纯路径;非回环返回 null
toPathAndQuery(url) 任意绝对地址剥成 path+query;相对路径原样返回(H5 用)
withBase(base, path) 相对路径拼到 base;已绝对 / data: / blob: 原样返回(幂等)
absoluteFileUrl(path) stripLoopbackHost 之后按 apiBaseUrl() 重拼 —— 当前线路地址
wanFileUrl(path) 把任意地址的 path+query 换到 WebSitePolicy.WebApiRoot —— 公网地址
absoluteFileUrl(path) H5:剥成 path+query 按页面 origin 重拼;App:stripLoopbackHost 后按 apiBaseUrl() 重拼
wanFileUrl(path) H5:原样返回(本就锁公网);App:把任意地址的 path+query 换到 WebSitePolicy.WebApiRoot
normalizePayloadFileUrls(data) 响应层统一收口:递归改写返回体里「错主机的绝对 /FileServer/ 直链」到 fileUrlBase()(见下)
```
`apiBaseUrl()` 来自访问线路(`api-endpoint-setting`):`lan` 模式返回用户在「我的 → 访问线路」填的内网地址,否则返回公网(App 开发构建取 `WebApiRootDevApp`,发布构建取 `WebApiRoot`)。
**统一入口(单一文件收口)**:归一规则全部住在 `utils/fileUrl.ts` 一个文件;`common/http.interceptor.ts` 的成功分支只调用一次 `normalizePayloadFileUrls(rawData)`。它只改写同时满足「绝对 http(s) + 路径含 `/FileServer/` + 主机与基座不符」的字符串,因此:
- 会改:`https://10.1.1.163/FileServer/...`(UAT 反代未传 Host)→ 按基座(H5=origin / App=线路)重拼;
- 不动:提交用的相对路径 `/FileServer/...` 与裸物理名(OrderKey/FileName,无主机)、`blob:`/`data:`/本地临时路径、主机本就正确的直链——避免污染保存回传;
- 效果:H5 侧**新增展示点即使忘了调 `absoluteFileUrl`,数据已在响应层被洗干净**,不会再打到内网 IP。App 相对路径展示仍需 `absoluteFileUrl`(响应层故意不改相对路径,否则破坏提交键)。
**端间策略差异(固定口径)**:
- **H5(微信内置浏览器等)只能走公网域名(如 `https://uat.youjiala.com`),不得变更**:没有内网直连概念,文件地址一律剥成 path+query 后按当前页面 origin 重拼。这样即使后端按请求 Host 拼出了内网 IP(UAT 反代没传原始 Host 时会出 `https://10.1.1.163/FileServer/...`,外部浏览器 `ERR_CONNECTION_REFUSED`),也会被改回可达的公网地址;
- **App 保留「访问线路」自定义外网/内网切换**:行为不变,资源地址跟随当前线路。
`apiBaseUrl()` 来自访问线路(`api-endpoint-setting`,仅 App 生效):`lan` 模式返回用户在「我的 → 访问线路」填的内网地址,否则返回公网(App 开发构建取 `WebApiRootDevApp`,发布构建取 `WebApiRoot`)。
实测映射(Node 侧验证过同一套逻辑):
| 输入 | `absoluteFileUrl`(内网 172.16.253.53:5235) | `wanFileUrl` |
| 输入 | `absoluteFileUrl`(App 内网 172.16.253.53:5235) | `wanFileUrl` |
| --- | --- | --- |
| `http://localhost:5235/FileServer/Scale/...` | `http://172.16.253.53:5235/FileServer/Scale/...` | `https://uat.youjiala.com/FileServer/Scale/...` |
| `/FileServer/Scale/...` | `http://172.16.253.53:5235/FileServer/Scale/...` | `https://uat.youjiala.com/FileServer/Scale/...` |
| `http://172.16.253.53:5235/...?v=1` | 原样 | `https://uat.youjiala.com/...?v=1` |
| `https://uat.youjiala.com/...` | 原样 | 原样(调用方判重后不再重试) |
| `https://10.1.1.163/FileServer/...`(UAT 反代未传 Host) | 原样(内网 IP 非回环,App 在内网可达时本就应用它) | 换到 `https://uat.youjiala.com/FileServer/...` |
| `""` / `undefined` | `""` | `""` |
H5 下 `absoluteFileUrl` 与输入主机无关,恒为「页面 origin + path+query」;`wanFileUrl` 原样返回。
## 5. 消费面:哪些地方必须过
| 位置 | 处理 |
@@ -65,12 +81,12 @@ wanFileUrl(path) 把任意地址的 path+query 换到 WebSitePolicy.WebA
| `composables/useMediaShow.ts` `loadPhotos` 的 list 分支与相册明细分支 | `url` / `thumb` / `full` 全部过 `absoluteFileUrl` |
| `composables/useMediaShow.ts` `loadTracks` / `loadVideos` | `url`、`pic`、`lrc`、`m3u8` 全部过 `absoluteFileUrl`(调试态下它们同样是 localhost 前缀) |
纪律:**凡是渲染后端文件地址的字段,一律先过 `absoluteFileUrl`;可交互/可预览的图片再挂一层 `@error` 回落 `wanFileUrl`。** 不要把 `FormattedPic` / `FileName` / `FormattedUrl` 直接绑到 `src`。
纪律:**凡是渲染后端文件地址的字段,一律先过 `absoluteFileUrl`;可交互/可预览的图片再挂一层 `@error` 回落 `wanFileUrl`。** 不要把 `FormattedPic` / `FileName` / `FormattedUrl` 直接绑到 `src`。(注:错主机绝对直链已由响应层 `normalizePayloadFileUrls` 全局兜底,H5 忘记套也不会打内网 IP;但 App 相对路径展示、以及 `@error` 公网回落仍需按本纪律逐点处理。)
## 6. 新增字段时的固定步骤
1. 确认接口返回的字段是路径还是完整 URL(看 uat 实际响应,不要按字段名猜);
2. 渲染前套 `absoluteFileUrl`;
2. 渲染前套 `absoluteFileUrl`(错主机绝对直链已由响应层 `normalizePayloadFileUrls` 全局兜底,H5 新展示点忘套也不会打内网 IP;App 相对路径展示仍需显式套);
3. 图片类再加 `@error` → `wanFileUrl` 一次回落 → 仍失败才占位;
4. `npx vue-tsc --noEmit` 退出码 0;
5. App 端(尤其内网线路)实际看一次,H5 正常不代表 App 正常。
@@ -82,10 +98,10 @@ wanFileUrl(path) 把任意地址的 path+query 换到 WebSitePolicy.WebA
- 内网线路取不到:确认手机与开发机同一 Wi-Fi、后端监听局域网地址(不是只监听 `127.0.0.1`)、`D:\Net8\FileServer` 下有该文件;
- 回落到公网仍失败:检查 `WebSitePolicy.WebApiRoot`(`.env` 的 `VITE_WEB_API_ROOT`)与 uat 上文件是否存在。
## 8. 可选的后端根治
## 8. 后端根治(可选,与前端兜底并行)
当前前端已经兜住三种形态,后端改不改都能用。若要少一层兜底,任一即可:
当前前端已经兜住三种形态 + 内网 IP 误拼(H5 按 origin 重拼),后端改不改都能用。若要少一层兜底,按优先级:
- **UAT nginx 反代补 `proxy_set_header Host $host;`(治本)**:`FileTypePolicy.BuildApplicationUrl` 按请求 Host 拼地址,反代不传原始 Host 时 Kestrel 看到的是 `proxy_pass` 目标内网 IP(如 `10.1.1.163`),拼出的绝对地址外部不可达;
- 把 `appsettings.json` 的 `ApplicationUrl` 从 `http://localhost:5235` 改成开发机的局域网地址(如 `http://172.16.253.53:5235`),调试态直接给手机可达地址;
- 或让 `PackImageScareFileName` 按当前请求的 Scheme+Host 拼前缀(与 `FileTypePolicy.BuildApplicationUrl` 的口径一致),线上也返回绝对地址;
- 多实例部署时让各实例的 `PhysicalFileDirectory` 指向同一份共享存储,否则文件只会留在上传它的那一台上。