React Native对接Node/Express/MongoDB出现Axios Network Error
当前开发的移动端配套服务应用技术栈如下:
- 客户端:React Native Expo-cli
- 服务端:Node.js + Express
- 数据库:MongoDB
使用实体Android、iOS设备运行客户端时,客户端与服务端连接异常。经SSH登录部署服务的Linux服务器核查:服务端实际公网IP为102.249.18.175,服务监听端口为443,已成功连接MongoDB、进程正常启动。前端请求地址填写的IP为192.249.18.175,与服务端实际IP存在明显差异。
前端核心登录请求代码
try { const response = await axios.post( "http://192.249.18.175:443/userAuth/signin", { email, password, } ); console.log("signin response: ", response); if (response.data.succ) { return Alert.alert("로그인 성공"); } else { // login fail ... return Alert.alert("로그인 정보가 맞지 않습니다, 다시 입력해주세요"); } } catch (e) { const errorResponse = (e as AxiosError).response; console.error(errorResponse); // if (errorResponse) { // Alert.alert("알림", errorResponse.data.message); // } console.log("Hello world!"); return Alert.alert(`Error: ${JSON.stringify(e.response)}`); }
后端对应登录接口代码
app.post("/userAuth/signin", async (req, res) => { const { email, password } = req.body; console.log(req.body); isLocated = await userAuth.find({ email: email, password: password }); if (isLocated) { res.status(200).json({ succ: true }); } else { res.status(404).json({ succ: false }); } });
客户端控制台报错
核心报错为无法连接到192.249.18.175的443端口,请求状态码为0,无响应头:
Hello world! XMLHttpRequest { "DONE": 4, "HEADERS_RECEIVED": 2, "LOADING": 3, "OPENED": 1, "UNSENT": 0, "_aborted": false, "_cachedResponse": undefined, "_hasError": true, "_headers": Object { "accept": "application/json, text/plain, */*", "content-type": "application/json", }, "_incrementalEvents": false, "_lowerCaseResponseHeaders": Object {}, "_method": "POST", "_perfKey": "network_XMLHttpRequest_http://192.249.18.175:443/userAuth/signin", "_performanceLogger": PerformanceLogger { "_closed": false, "_extras": Object {}, "_pointExtras": Object {}, "_points": Object { "initializeCore_end": 1657466704543, "initializeCore_start": 1657466704328, }, "_timespans": Object { "network_XMLHttpRequest_http://143.248.219.186:19000/logs": Object { "endExtras": undefined, "endTime": 1657466705712, "startExtras": undefined, "startTime": 1657466705643, "totalTime": 69, }, "network_XMLHttpRequest_http://143.248.219.186:19000/symbolicate": Object { "endExtras": undefined, "endTime": 1657466844264, "startExtras": undefined, "startTime": 1657466844224, "totalTime": 40, }, "network_XMLHttpRequest_http://192.249.18.175:443/userAuth/signin": Object { "startExtras": undefined, "startTime": 1657466713331, }, }, }, "_requestId": null, "_response": "Failed to connect to /192.249.18.175:443", "_responseType": "", "_sent": true, "_subscriptions": Array [], "_timedOut": false, "_trackingName": "unknown", "_url": "http://192.249.18.175:443/userAuth/signin", "data": undefined, "readyState": 4, "responseHeaders": undefined, "status": 0, "timeout": 0, "upload": XMLHttpRequestEventTarget {}, "withCredentials": true, } at node_modules\expo\build\environment\react-native-logs.fx.js:27:4 in error at node_modules\@babel\runtime\helpers\regeneratorRuntime.js:86:13 in tryCatch at node_modules\@babel\runtime\helpers\regeneratorRuntime.js:66:31 in <anonymous> at node_modules\@babel\runtime\helpers\regeneratorRuntime.js:86:13 in tryCatch at node_modules\@babel\runtime\helpers\regeneratorRuntime.js:124:27 in invoke at node_modules\@babel\runtime\helpers\regeneratorRuntime.js:132:16 in PromiseImpl.resolve.then$argument_1 at node_modules\react-native\node_modules\promise\setimmediate\core.js:37:13 in tryCallOne at node_modules\react-native\node_modules\promise\setimmediate\core.js:123:24 in setImmediate$argument_0 at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:248:12 in _allocateCallback$argument_0 at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:112:14 in _callTimer at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:162:14 in _callReactNativeMicrotasksPass at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:413:41 in callReactNativeMicrotasks at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:391:6 in __callReactNativeMicrotasks at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:133:6 in __guard$argument_0 at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:368:10 in __guard at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:132:4 in flushedQueue
服务端启动日志
可确认服务正常启动且已成功连接MongoDB:
[nodemon] 2.0.19 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): *.* [nodemon] watching extensions: js,mjs,json [nodemon] starting `node index.js` Server started at 443 connected to mongoDB
按优先级从高到低排查:
修正错误的请求IP
前端代码里写的请求IP192.249.18.175和服务端实际公网IP102.249.18.175第一位就写错了,公网不存在192.249.18.175这个可路由的地址,先把axios请求地址里的IP改成服务端实际公网IP102.249.18.175,这是当前最直接的错误。匹配请求协议和端口
当前前端用http://协议请求443端口,443是HTTPS协议默认端口,HTTP默认端口为80。如果服务端没有配置SSL证书启用HTTPS,要么把服务监听端口改成80,前端用http://加80端口请求;要么给服务端配置合法SSL证书后,前端改用https://开头的地址请求443端口,协议端口不匹配会直接导致连接失败。检查服务端监听地址
确认Express服务启动时不是仅绑定本地回环地址127.0.0.1,需要绑定0.0.0.0才能接收外部设备的请求,正确的启动写法参考:app.listen(443, '0.0.0.0', () => { console.log('Server started at 443') })放通服务器端口访问规则
检查Linux服务器本地防火墙(firewalld/ufw/iptables)是否放通对应服务端口的入站规则,同时检查云服务商控制台的安全组配置,确认对应端口允许公网入方向访问,测试阶段可临时放通所有源IP验证连通性,验证通过后再收紧规则。处理移动端系统网络限制
Android 9及以上、iOS 9及以上版本默认禁止明文HTTP请求,如果开发阶段临时用HTTP协议调试,需要:- Android端:添加网络安全配置文件,允许对应域名/IP的明文流量
- iOS端:在Info.plist中配置App Transport Security例外,允许HTTP请求
修复后端逻辑bug
Mongoose的find()方法返回值始终是数组,查询无匹配结果时返回空数组,而空数组在JS中属于truthy值,会导致即使账号密码错误也会返回登录成功。需要修改判断逻辑为:isLocated = await userAuth.find({ email: email, password: password }); if (isLocated.length > 0) { res.status(200).json({ succ: true }); } else { res.status(404).json({ succ: false }); }另外注意明文存储密码存在安全风险,生产环境必须对密码做哈希加盐处理。
内容的提问来源于stack exchange,提问作者안태찬

