yeg
2026-09-14 23bf78e891869d38e26baec3b42231a7488e2525
fix(miniprogram): 开发环境地址改用回环地址,不再猜本机 IP

上一版用 uni.getNetworkType 读本机局域网 IP 自动探测,实测在微信开发者工具里拿不到 ip
字段,于是走了兜底值,请求仍然打到 192.168.110.26:8080(本机实际是 192.168.110.15),
"登录不上"没有解决。另外真机上该字段返回的是手机自己的地址,对连开发机也没有帮助。

结论是不要猜 IP:微信开发者工具与后台跑在同一台机器上,127.0.0.1 永远可达;页面来源本身
也是 127.0.0.1(请求 Referer 为 http://127.0.0.1:42665),所以这条路径跟网络怎么变无关。

改动:
- development-server.uts 简化为:默认 http://127.0.0.1:8080;真机调试时用 storage 覆盖
(uni.setStorageSync('development-api-base-url', 'http://<开发机局域网IP>:8080')),
清掉该 key 即回到默认值——换环境不需要改代码、不需要重新编译。
- 去掉 getNetworkType 探测与"记住上次成功地址"的兜底逻辑。
- App.uvue 仍然在 onLaunch 调一次初始化,作用改为把当前生效地址打进日志,便于排查。

跨域仍需上一版修复的 CORS 通配(http://127.0.0.1:[*]),因为页面在 127.0.0.1:随机端口。
2个文件已修改
150 ■■■■■ 已修改文件
rencaika/config/development-server.uts 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rencaika_merchant/config/development-server.uts 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rencaika/config/development-server.uts
@@ -1,66 +1,37 @@
/**
 * 开发环境服务端地址解析。
 *
 * 为什么需要这个文件:后台跑在开发机上,而开发机的局域网 IP 会随网络环境变化
 * (见过 192.168.110.26 与 192.168.110.15)。把 IP 写死在 config/api.uts 里,
 * 每次换网就得改代码、重新编译,否则小程序请求会打到一台不存在的机器上,
 * 表现成"登录不上"——很难一眼看出是地址问题。
 * 背景:后台跑在开发机上,而开发机的局域网 IP 会随网络环境变化(见过 192.168.110.26 与
 * 192.168.110.15)。把 IP 写死在 config/api.uts 里,每次换网都得改代码重新编译,忘了改就
 * 请求到一台不存在的机器上,表现成"登录不上"——从报错里看不出是地址问题。
 *
 * 这里的做法:开发环境下在 App 启动时读一次本机网络接口,把真正的局域网 IP 缓存下来,
 * 之后所有请求都用它。检测失败(真机、平台不支持)就退回默认值并记住上次成功的地址,
 * 所以不会比写死更差,但正常情况下**不需要再改代码**。
 * 结论:**不要猜 IP**。微信开发者工具与后台跑在同一台机器上,所以 127.0.0.1 永远可达;
 * 页面来源本身也是 127.0.0.1(见请求的 Referer),因此同源、连跨域都不涉及。
 *
 * 曾试过用 uni.getNetworkType 读本机局域网 IP 自动探测,实测在开发者工具里拿不到 ip
 * (真机上该字段返回的是手机自己的地址,对连开发机也没有帮助),所以放弃这条路。
 *
 * 真机调试:手机访问不了 127.0.0.1。此时用 storage 覆盖即可,不必改代码:
 *   uni.setStorageSync('development-api-base-url', 'http://<开发机局域网IP>:8080')
 * 清了这条 key 就回到开发者工具适用的默认值。
 */
/** 检测失败时的兜底地址;真机调试时也可以直接改这里。 */
const defaultDevelopmentBaseUrl = 'http://192.168.110.26:8080'
/** 开发者工具与本机后台:回环地址,跟网络怎么变无关。 */
const loopbackDevelopmentBaseUrl = 'http://127.0.0.1:8080'
const detectedUrlStorageKey = 'development-api-base-url'
/** 覆盖用的 storage key,真机调试时写入开发机局域网地址。 */
const overrideStorageKey = 'development-api-base-url'
let resolvedDevelopmentBaseUrl = ''
/**
 * 读取本机局域网 IP 组装服务端地址。
 *
 * `uni.getNetworkType` 在部分平台会带回 `ip`(本机局域网地址),但 uni-app 的类型里
 * 没有声明这个可选字段,所以先转成 any 再读,避免 uvue 的严格类型检查报错。
 */
const detectDevelopmentBaseUrl = (onDone: (url: string) => void) => {
    try {
        uni.getNetworkType({
            success: (result: any) => {
                const ip = result != null && result.ip != null ? (result.ip as string).trim() : ''
                if (ip.length > 0) {
                    onDone('http://' + ip + ':8080')
                    return
                }
                onDone('')
            },
            fail: (_error: any) => {
                onDone('')
            }
        } as any)
    } catch (_error) {
        onDone('')
    }
/** 开发环境基础地址:先看真机调试的覆盖值,否则用回环地址。 */
export const developmentApiBaseUrl = (): string => {
    const override = uni.getStorageSync(overrideStorageKey)
    if (override != null && (override as string).trim().length > 0) return (override as string).trim()
    return loopbackDevelopmentBaseUrl
}
/**
 * 在 App 启动时调用一次。检测成功会用检测结果,否则用上次成功的地址,
 * 再否则用兜底地址。同步先把兜底值填上,避免启动瞬间的请求拿到空地址。
 * 在 App 启动时调用。这里只做一次记录,方便排查"请求打到哪台机器"。
 */
export const initializeDevelopmentApiBaseUrl = () => {
    const remembered = uni.getStorageSync(detectedUrlStorageKey)
    resolvedDevelopmentBaseUrl = (remembered != null && (remembered as string).length > 0)
        ? (remembered as string)
        : defaultDevelopmentBaseUrl
    detectDevelopmentBaseUrl((url: string) => {
        if (url.length == 0) return
        resolvedDevelopmentBaseUrl = url
        uni.setStorageSync(detectedUrlStorageKey, url)
    })
}
/** 开发环境基础地址:未初始化时退回默认值,保证任何调用点都拿得到非空地址。 */
export const developmentApiBaseUrl = (): string => {
    return resolvedDevelopmentBaseUrl.length > 0 ? resolvedDevelopmentBaseUrl : defaultDevelopmentBaseUrl
    console.log('Development API base url: ' + developmentApiBaseUrl())
}
rencaika_merchant/config/development-server.uts
@@ -1,66 +1,37 @@
/**
 * 开发环境服务端地址解析。
 *
 * 为什么需要这个文件:后台跑在开发机上,而开发机的局域网 IP 会随网络环境变化
 * (见过 192.168.110.26 与 192.168.110.15)。把 IP 写死在 config/api.uts 里,
 * 每次换网就得改代码、重新编译,否则小程序请求会打到一台不存在的机器上,
 * 表现成"登录不上"——很难一眼看出是地址问题。
 * 背景:后台跑在开发机上,而开发机的局域网 IP 会随网络环境变化(见过 192.168.110.26 与
 * 192.168.110.15)。把 IP 写死在 config/api.uts 里,每次换网都得改代码重新编译,忘了改就
 * 请求到一台不存在的机器上,表现成"登录不上"——从报错里看不出是地址问题。
 *
 * 这里的做法:开发环境下在 App 启动时读一次本机网络接口,把真正的局域网 IP 缓存下来,
 * 之后所有请求都用它。检测失败(真机、平台不支持)就退回默认值并记住上次成功的地址,
 * 所以不会比写死更差,但正常情况下**不需要再改代码**。
 * 结论:**不要猜 IP**。微信开发者工具与后台跑在同一台机器上,所以 127.0.0.1 永远可达;
 * 页面来源本身也是 127.0.0.1(见请求的 Referer),因此同源、连跨域都不涉及。
 *
 * 曾试过用 uni.getNetworkType 读本机局域网 IP 自动探测,实测在开发者工具里拿不到 ip
 * (真机上该字段返回的是手机自己的地址,对连开发机也没有帮助),所以放弃这条路。
 *
 * 真机调试:手机访问不了 127.0.0.1。此时用 storage 覆盖即可,不必改代码:
 *   uni.setStorageSync('development-api-base-url', 'http://<开发机局域网IP>:8080')
 * 清了这条 key 就回到开发者工具适用的默认值。
 */
/** 检测失败时的兜底地址;真机调试时也可以直接改这里。 */
const defaultDevelopmentBaseUrl = 'http://192.168.110.26:8080'
/** 开发者工具与本机后台:回环地址,跟网络怎么变无关。 */
const loopbackDevelopmentBaseUrl = 'http://127.0.0.1:8080'
const detectedUrlStorageKey = 'development-api-base-url'
/** 覆盖用的 storage key,真机调试时写入开发机局域网地址。 */
const overrideStorageKey = 'development-api-base-url'
let resolvedDevelopmentBaseUrl = ''
/**
 * 读取本机局域网 IP 组装服务端地址。
 *
 * `uni.getNetworkType` 在部分平台会带回 `ip`(本机局域网地址),但 uni-app 的类型里
 * 没有声明这个可选字段,所以先转成 any 再读,避免 uvue 的严格类型检查报错。
 */
const detectDevelopmentBaseUrl = (onDone: (url: string) => void) => {
    try {
        uni.getNetworkType({
            success: (result: any) => {
                const ip = result != null && result.ip != null ? (result.ip as string).trim() : ''
                if (ip.length > 0) {
                    onDone('http://' + ip + ':8080')
                    return
                }
                onDone('')
            },
            fail: (_error: any) => {
                onDone('')
            }
        } as any)
    } catch (_error) {
        onDone('')
    }
/** 开发环境基础地址:先看真机调试的覆盖值,否则用回环地址。 */
export const developmentApiBaseUrl = (): string => {
    const override = uni.getStorageSync(overrideStorageKey)
    if (override != null && (override as string).trim().length > 0) return (override as string).trim()
    return loopbackDevelopmentBaseUrl
}
/**
 * 在 App 启动时调用一次。检测成功会用检测结果,否则用上次成功的地址,
 * 再否则用兜底地址。同步先把兜底值填上,避免启动瞬间的请求拿到空地址。
 * 在 App 启动时调用。这里只做一次记录,方便排查"请求打到哪台机器"。
 */
export const initializeDevelopmentApiBaseUrl = () => {
    const remembered = uni.getStorageSync(detectedUrlStorageKey)
    resolvedDevelopmentBaseUrl = (remembered != null && (remembered as string).length > 0)
        ? (remembered as string)
        : defaultDevelopmentBaseUrl
    detectDevelopmentBaseUrl((url: string) => {
        if (url.length == 0) return
        resolvedDevelopmentBaseUrl = url
        uni.setStorageSync(detectedUrlStorageKey, url)
    })
}
/** 开发环境基础地址:未初始化时退回默认值,保证任何调用点都拿得到非空地址。 */
export const developmentApiBaseUrl = (): string => {
    return resolvedDevelopmentBaseUrl.length > 0 ? resolvedDevelopmentBaseUrl : defaultDevelopmentBaseUrl
    console.log('Development API base url: ' + developmentApiBaseUrl())
}