【发布时间】:2020-08-04 00:56:36
【问题描述】:
目标:创建一个可以相互通信的前端和后端。
我从前端开始,使用https://reactnative.dev/movies.json 作为占位符来提供前端数据。一旦成功完成,我就开始开发服务器,即使它在 Postman 中工作,它在前端也不能工作。
问题:当我将https://reactnative.dev/movies.json 替换为http://xxx.xxx.xxx.x:5000/movies.json 时,后端和前端无法通信。
到目前为止的测试:
我的 react native 应用可以成功GEThttps://reactnative.dev/movies.json 构建的 reactnative.dev JSON 电影列表,并会显示电影列表。
我已经使用 Postman 测试了后端,并验证了 http://xxx.xxx.xxx.x:5000/movies.json 返回的 JSON 响应与 https://reactnative.dev/movies.json 完全相同
前端(工作 - 可以 GET 来自 https://reactnative.dev/movies.json):
import React from 'react';
import { StyleSheet, Text, View, ActivityIndicator, Button } from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: null,
};
}
componentDidMount(){
return fetch('https://reactnative.dev/movies.json')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson.movies,
})
})
.catch((error) => {
console.log(error);
});
}
render () {
if(this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator/>
</View>
)
} else {
let movies = this.state.dataSource.map((val, key) => {
return <View key={key} style={styles.item}>
<Text>{val.title}</Text>
</View>
});
return (
<View style={styles.container}>
{movies}
</View>
)
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
服务器结构:
/backend
--/data
--movies.json
--/routes
--movies.js
--routes.js
server.js
package.json
package-lock.json
server.cert
server.key
.env
.gitignore
/data/movies.json
{
"title": "The Basics - Networking",
"description": "Your app fetched this from a remote endpoint!",
"movies": [
{ "id": "1", "title": "Star Wars", "releaseYear": "1977" },
{ "id": "2", "title": "Back to the Future", "releaseYear": "1985" },
{ "id": "3", "title": "The Matrix", "releaseYear": "1999" },
{ "id": "4", "title": "Inception", "releaseYear": "2010" },
{ "id": "5", "title": "Interstellar", "releaseYear": "2014" }
]
}
/routes/movies.js
const userRoutes = (app, fs) => {
// variables
const dataPath = "./data/movies.json";
// READ
app.get("/movies.json", (req, res) => {
fs.readFile(dataPath, "utf8", (err, data) => {
if (err) {
throw err;
}
res.send(JSON.parse(data));
});
});
};
module.exports = userRoutes;
/routes/routes.js
// load up our shiny new route for users
const userRoutes = require("./movies");
const appRouter = (app, fs) => {
// we've added in a default route here that handles empty routes
// at the base API url
app.get("/", (req, res) => {
res.send("welcome to the development api-server");
});
// run our user route module here to complete the wire up
userRoutes(app, fs);
};
// this line is unchanged
module.exports = appRouter;
server.js
// load up the express framework and body-parser helper
const express = require("express");
const bodyParser = require("body-parser");
// create an instance of express to serve our end points
const app = express();
// we'll load up node's built in file system helper library here
// (we'll be using this later to serve our JSON files
const fs = require("fs");
// configure our express instance with some body-parser settings
// including handling JSON data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// this is where we'll handle our various routes from
const routes = require("./routes/routes.js")(app, fs);
// finally, launch our server on port 5000.
const server = app.listen(5000, () => {
console.log("listening on port %s...", server.address().port);
});
控制台错误输出:
Network request failed
- node_modules\whatwg-fetch\dist\fetch.umd.js:511:17 in setTimeout$argument_0
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:135:14 in _callTimer
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:387:16 in callTimers
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:425:19 in __callFunction
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:112:6 in __guard$argument_0- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:373:10 in __guard
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:111:4 in callFunctionReturnFlushedQueue
* [native code]:null in callFunctionReturnFlushedQueue
更新了服务器以使用 HTTPS:
// load up the express framework and body-parser helper
const express = require("express");
const bodyParser = require("body-parser");
// create an instance of express to serve our end points
const app = express();
// we'll load up node's built in file system helper library here
// (we'll be using this later to serve our JSON files
const fs = require("fs");
const https = require('https');
// configure our express instance with some body-parser settings
// including handling JSON data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// this is where we'll handle our various routes from
const routes = require("./routes/routes.js")(app, fs);
// finally, launch our server on port 5000.
const server = https.createServer({
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.cert')
}, app).listen(5000, () => {
console.log("listening on port %s...", server.address().port);
});
HTTPS 更新后的控制台错误输出:
Network request failed
- node_modules\whatwg-fetch\dist\fetch.umd.js:505:17 in setTimeout$argument_0
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:135:14 in _callTimer
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:387:16 in callTimers
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:425:19 in __callFunction
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:112:6 in __guard$argument_0- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:373:10 in __guard
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:111:4 in callFunctionReturnFlushedQueue
* [native code]:null in callFunctionReturnFlushedQueue
在 Postman 中对 HTTPS 服务器的测试结果:
GET 请求有效,但是我必须将 Postman 设置为接受自签名证书。
我运行expo eject 来公开整个Info.plist NSAppTransportSecurity 的键和字典已经在Info.plist 中设置在下面:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>csharp_frontend</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>xxx.xxx.xxx.x</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string/>
<key>UILaunchStoryboardName</key>
<string>SplashScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDefault</string>
<key>UIRequiresFullScreen</key>
<true/>
</dict>
</plist>
更新了 Info.plist 以包含服务器的 IP 地址而不是 localhost
<dict>
<key>xxx.xxx.xxx.x</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
错误略有改变 - 新错误 - '373:10 in __guard'
Network request failed
- node_modules\whatwg-fetch\dist\fetch.umd.js:505:17 in setTimeout$argument_0
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:135:14 in _callTimer
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:387:16 in callTimers
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:425:19 in __callFunction
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:112:6 in __guard$argument_0
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:373:10 in __guard
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:111:4 in callFunctionReturnFlushedQueue
* [native code]:null in callFunctionReturnFlushedQueue
可能是问题:我认为由于 SSL 证书是自签名的,因此反应原生前端将请求视为 HTTP 而不是 HTTPS。
有什么想法吗??
【问题讨论】:
-
console.log(error)显示什么?xxx.xxx.xxx.x是 IP 地址吗?如果是,移动客户端是否在同一个网络上 -
也可能是因为你已经从 HTTPS 转到了 HTTP ~ github.com/facebook/react-native/issues/24408
-
嗨@Phil,我刚刚在
console.log(error)输出中添加。是的xxx.xxx.xxx.x代表我的IP 地址,是的,移动客户端在同一个网络上。嗯,这是一个很好的发现,我在 iOS 上,可能是同样的问题,如果这是解决方案,我会调查并更新问题。谢谢。
标签: javascript node.js json react-native express