React Native Expo生产环境中Axios网络错误导致无法连接服务器
我完全懂你现在的崩溃——本地开发时不管连本地还是远程服务器都顺得不行,结果打包成APK/AAB做内部测试就直接卡壳,Sentry还只抛出个模糊的「Network Error」,连具体原因都不给,真的太闹心了!我来帮你一步步拆解可能的问题,再给你针对性的解决方案。
首先,排查最常见的几个核心原因
1. Expo Config的API URL在生产打包时未正确注入
你用Constants.expoConfig.extra.apiUrl作为baseURL,这在本地开发时没问题,但生产打包时如果配置不对,很可能指向了无法访问的地址(比如localhost或者内网IP,打包后的设备根本访问不到)。
解决方案:
- 检查你的
eas.json(如果用EAS Build),确保对应生产环境的env里配置了正确的公网API地址:// eas.json示例 { "build": { "production": { "env": { "API_URL": "https://your-public-api-domain.com" } } } } - 同时在
app.config.js里确保extra.apiUrl会读取环境变量:export default ({ config }) => { return { ...config, extra: { apiUrl: process.env.API_URL || "http://localhost:3000", // 本地 fallback }, }; }; - 打包后可以在Sentry的错误日志里额外捕获
baseURL,确认是否正确:
在请求拦截器里加一行:Sentry.setExtra('api_base_url', config.baseURL);
2. Android网络安全配置拦截了请求
Android 9(API 28)及以上默认会阻止明文HTTP请求或自签名HTTPS证书的请求,这是最容易被忽略的点!
解决方案:
- 如果你的服务器用的是HTTP(仅限测试,生产务必用HTTPS),在
app.json里添加Android网络安全配置,允许明文流量:"android": { "networkSecurityConfig": { "domainConfigs": [ { "domain": "your-api-domain.com", "cleartextTrafficPermitted": true } ] } } - 如果是自签名HTTPS证书,需要把证书的PEM文件放到项目中,然后在
networkSecurityConfig里引用(具体可以查Expo官方文档的Android网络安全配置)。
3. Axios的XHR适配器在生产环境的兼容性问题
Axios默认在React Native中使用XMLHttpRequest适配器,但某些版本的Axios在Expo生产打包后可能出现兼容性问题,导致请求被静默拦截。
解决方案:
- 切换到Fetch适配器替代XHR:
先安装适配库:
然后修改你的Axios实例创建代码:npm install @vespaiach/axios-fetch-adapterimport fetchAdapter from '@vespaiach/axios-fetch-adapter'; const api = axios.create({ baseURL: Constants.expoConfig.extra.apiUrl, timeout: 10000, headers: { 'Content-Type': 'application/json' }, adapter: fetchAdapter, // 替换为Fetch适配器 }); - 调整Axios版本:如果是版本冲突问题,可以尝试降级到
1.6.0(这个版本在RN/Expo环境下比较稳定),或者升级到最新的稳定版,避免用太新的测试版。
4. 请求拦截器的异步操作拖慢/阻塞请求
你的请求拦截器里每次都用await AsyncStorage.getItem('user'),虽然Axios支持异步拦截器,但在生产环境下,频繁的AsyncStorage异步调用可能导致请求时序问题,甚至被系统中断。
解决方案:
- 把Token缓存到内存中,减少AsyncStorage的调用:
// 全局变量缓存Token,登录成功后更新它 let cachedAuthToken = null; // 比如在登录成功后: const saveUserToken = async (token) => { cachedAuthToken = token; await AsyncStorage.setItem('user', JSON.stringify({ token })); }; // 修改请求拦截器: api.interceptors.request.use( async (config) => { try { // 只有缓存为空时才去AsyncStorage取 if (!cachedAuthToken) { const userData = await AsyncStorage.getItem('user'); if (userData) { const { token } = JSON.parse(userData); cachedAuthToken = token; } } if (cachedAuthToken) { config.headers.Authorization = `Bearer ${cachedAuthToken}`; } return config; } catch (error) { Sentry.captureException(error, { extra: { context: 'request_interceptor' } }); return config; } }, // 错误处理部分不变 );
5. 服务器端的防火墙/CDN拦截了请求
有时候问题根本不在客户端——你的服务器可能配置了防火墙、WAF或者CDN规则,只允许特定IP(比如你的开发机)访问,而移动设备的IP被拦截了,导致返回网络错误。
解决方案:
- 用手机的浏览器直接访问你的API地址,看看能不能正常返回数据;
- 检查服务器的防火墙规则、CDN的访问控制列表,确保移动设备的IP可以正常访问;
- 确认服务器的SSL证书是否有效(比如是否过期、是否被浏览器/设备信任)。
最后,给你一个快速调试技巧
在生产打包的APK里临时添加调试日志,或者在Sentry的错误捕获里补充更多上下文:
比如在响应拦截器的Network Error分支里,把当前的baseURL、设备网络状态、请求参数都传到Sentry:
if (message === 'Network Error') { const state = await NetInfo.fetch(); Sentry.captureException(error, { extra: { context: 'network_error_debug', base_url: Constants.expoConfig.extra.apiUrl, is_device_connected: state.isConnected, request_url: config?.url, request_method: config?.method } }); }
这样你就能在Sentry里看到更详细的信息,直接定位到底是地址错了、设备没网,还是服务器根本没收到请求。
内容来源于stack exchange

