Express Сервер не может получить запрос от приложения React Native - PullRequest
0 голосов
/ 04 августа 2020

Цель: создать интерфейс и серверную часть, которые могут взаимодействовать друг с другом. Я начал с внешнего интерфейса и использовал https://reactnative.dev/movies.json в качестве заполнителя для ввода данных внешнего интерфейса. Как только это было успешно завершено, я начал разрабатывать сервер, и хотя он работает в Postman, он не работает во внешнем интерфейсе.

Проблема: когда я заменяю https://reactnative.dev/movies.json на http://xxx.xxx.xxx.x:5000/movies.json, серверная часть и веб-интерфейс не может взаимодействовать.

Тестирование на данный момент: мое собственное приложение response может успешно GET список reactnative.dev JSON mov ie, созданный https://reactnative.dev/movies.json, и отобразит список фильмов.

Я протестировал бэкэнд с Postman и убедился, что http://xxx.xxx.xxx.x:5000/movies.json возвращает тот же ответ JSON, что и https://reactnative.dev/movies.json

Front End (Работает - может 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;

сервер. 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 Ключ и dict для 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 в __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.

Любые идеи ??

1 Ответ

1 голос
/ 04 августа 2020

Если вы работаете на платформе Android и используете уровень API> 27, я думаю, что эта проблема связана с ограничением протокола http в Android. В таком случае вам необходимо добавить android: usesCleartextTraffic = "true" в AndroidManifest. xml, чтобы разрешить http-трафик c:

<?xml version="1.0" encoding="utf-8"?>
<manifest...>
    <application
        ...
        android:usesCleartextTraffic="true"
        ...>
    </application>
</manifest>

В случае iOS версия> = 8

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
        ...
    </dict>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...