繁体   English   中英

React Native fetch() 网络请求失败

[英]React Native fetch() Network Request Failed

当我使用react-native init (RN 版本 0.29.1)创建一个全新的项目并将渲染方法中的提取放入公共 facebook 演示电影 API 时, Network Request Failed 有一个非常无用的堆栈跟踪,我无法在 chrome 控制台中调试网络请求。 这是我要发送的获取:

fetch('http://facebook.github.io/react-native/movies.json')
      .then((response) => response.json())
      .then((responseJson) => {
        return responseJson.movies;
      })
      .catch((error) => {
        console.error(error);
      });

这里的问题是iOS默认不允许HTTP请求,只允许HTTPS。 如果您想启用 HTTP 请求,请将其添加到您的info.plist

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

不建议允许所有域用于 http。 仅对必要的域进行例外处理。

来源: 在 iOS 9 和 OSX 10.11 中配置应用程序传输安全异常

将以下内容添加到应用的 info.plist 文件中:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>yourserver.com</key>
    <dict>
      <!--Include to allow subdomains-->
      <key>NSIncludesSubdomains</key>
      <true/>
      <!--Include to allow HTTP requests-->
      <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      <!--Include to specify minimum TLS version-->
      <key>NSTemporaryExceptionMinimumTLSVersion</key>
      <string>TLSv1.1</string>
    </dict>
  </dict>
</dict>

我使用localhost作为地址,这显然是错误的。 用服务器的IP地址(在模拟器所在的网络中)替换它后,它工作得很好。

编辑

在 Android Emulator 中,开发机器的地址是10.0.2.2 更多解释在这里

对于 Genymotion,地址是10.0.3.2 更多信息在这里

由于“http”,我在 Android 9 上遇到了同样的问题,只需在AndroidManifest.xml中添加android:usesCleartextTraffic="true" 即可解决问题

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
  android:usesCleartextTraffic="true"
 .......>
 .......
</application>

对我们来说,这是因为我们正在上传一个文件,而 RN filePicker 没有给出正确的 mime 类型。 它只是给了我们“图像”作为类型。 我们需要将其更改为“image/jpg”以使获取工作。

form.append(uploadFileName, {
  uri : localImage.full,
  type: 'image/jpeg',
  name: uploadFileName
 })

我在 Android 上遇到了同样的问题,但我设法找到了解决方案。 默认情况下,自 API 级别 28 以来,Android 会阻止明文流量(非 https 请求)。 但是,react-native 将 network-security-config 添加到调试版本( android/app/src/debug/res/xml/react_native_config.xml )中,它定义了一些域(localhost 和 AVD / Genymotion 的主机 IP),在开发模式下可以在没有 SSL 的情况下使用。 您可以在那里添加您的域以允许 http 请求。

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="false">localhost</domain>
    <domain includeSubdomains="false">10.0.2.2</domain>
    <domain includeSubdomains="false">10.0.3.2</domain>
    <domain includeSubdomains="true">dev.local</domain>
  </domain-config>
</network-security-config>

React Native Docs 给出了答案。

Apple 已阻止隐式明文 HTTP 资源加载。 所以我们需要在我们项目的 Info.plist(或等效)文件中添加以下内容。

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>localhost</key>
        <dict>
            <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>

React Native Docs -> 与现有应用程序集成 -> 测试您的集成 -> 添加应用程序传输安全异常

安卓用户:

  1. localhost替换为局域网 IP 地址,因为当您在 Android 设备上运行项目时,localhost 指向的是 Android 设备,而不是您的计算机,例如:将http://localost更改为http://192.168.1.123

  2. 如果您的请求 URL 是 HTTPS 并且您的 Android 设备在代理下,假设您在您的 Android 设备中安装了用户添加的 CA(如 burp suite 的 CA 或 Charles 的 CA),请确保您的 Android 版本低于Nougat(7.0) ,因为: Android Nougat 中受信任的证书颁发机构的更改

    用户添加的 CA
    保护所有应用程序数据是 Android 应用程序沙箱的一个关键目标。 Android Nougat 改变了应用程序与用户和管理员提供的 CA 交互的方式。 默认情况下,以 API 级别 24 为目标的应用程序在设计上不会支持此类 CA,除非应用程序明确选择加入。这种默认安全设置减少了应用程序的攻击面,并鼓励对网络和基于文件的应用程序数据进行一致处理。

问题可能出在服务器配置中。

Android 7.0 在此处描述了一个错误。 Vicky Chijwani 提出的解决方法:

将您的服务器配置为使用椭圆曲线 prime256v1。 例如,在 Nginx 1.10 中,您可以通过设置 ssl_ecdh_curve prime256v1;

我在Android上遇到了这个问题-

URL- localhost/authToken.json - 不起作用:(

URL- 10.106.105.103/authToken.json - 不起作用:(

URL- http://10.106.105.103/authToken.json - 工作:) :D

注意 - 在 Linux 上使用ifconfig或在 Windows 上使用ipconfig来查找机器 IpAddress

如果您使用 localhost 只需更改它:

从: http://localhost:3030到:http: http://10.0.2.2:3030 :3030

我在 Android Emulator 上遇到了同样的问题,我尝试使用有效证书访问外部 HTTPS URL。 但是在 react-native 中获取该 URL 失败

'fetch error:', { [TypeError: Network request failed]
sourceURL: 'http://10.0.2.2:8081/index.delta?platform=android&dev=true&minify=false' }

1)为了找出日志中的确切错误,我首先在应用程序上使用 Cmd + M 启用了“远程调试 JS”

2)报告的错误是

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

3) 我使用此方法添加了 URL 的有效证书 -> STEP 2

http://lpains.net/articles/2018/install-root-ca-in-android/

此证书被添加到用户选项卡。

4) 将属性android:networkSecurityConfig属性添加到 AndroidManifest.xml

添加网络安全配置文件res/xml/network_security_config.xml:

<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="user"/>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>

这应该可以工作并给您预期的响应。

对于Android,你可能错过了在AndroidManifest.xml中添加权限需要添加以下权限。

<uses-permission android:name="android.permission.INTERNET" /> 

我有类似的问题。 在我的情况下,对 localhost 的请求正在工作并突然停止。 事实证明,问题在于我在我的安卓手机上关闭了我的 wifi。

这对我有用,android 使用特殊类型的 IP 地址 10.0.2.2 然后是端口号

import { Platform } from 'react-native';

export const baseUrl = Platform.OS === 'android' ?
    'http://10.0.2.2:3000/'
: 
'http://localhost:3000/';

如果您将 docker 用于 REST api,对我来说,一个可行的案例是将主机名: http://demo.test/api替换为机器 ip 地址: http://xxxx/api 您可以通过检查无线网络上的 ipv4 来获取 IP。 你也应该有手机上的wifi。

您应该在 .then 中处理 fetch API 中的错误情况。

例如:

fetch(authURl,{ method: 'GET'})
.then((response) => {      
  const statusCode = response.status;
  console.warn('status Code',statusCode);
  if(statusCode==200){
    //success code
  }else{
    //handle other error code
  }      
},(err) => {
  console.warn('error',err)
})
.catch((error) => {
  console.error(error);
  return error;
});

通过在 0.0.0.0 上运行模拟服务器
注意:当您使用 json-server 运行另一台服务器时,这适用于 Expo。


另一种方法是在 0.0.0.0 而不是 localhost 或 127.0.0.1 上运行模拟服务器。

这使得模拟服务器可以在 LAN 上访问,并且因为 Expo 要求开发机器和运行 Expo 应用程序的移动设备位于同一网络上,所以模拟服务器也可以访问。

这可以在使用 json-server 时通过以下命令来实现

json-server --host 0.0.0.0 --port 8000 ./db.json --watch

访问此链接了解更多信息。

在我的情况下,我有 https url 但获取返回网络请求失败错误,所以我只是对正文进行字符串化,它的工作很有趣

 fetch('https://mywebsite.com/endpoint/', { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ firstParam: 'yourValue', secondParam: 'yourOtherValue' }) });

对我来说......我已经有https ......一旦我将'Content-type': 'application/json'添加到headers中,问题就消失了

headers: {
  Authorization: token,
  'Content-type': 'application/json',
  /** rest of headers... */
}

平台:安卓

不再允许HTTP 请使用HTTPS

从 Android API 28 和 iOS 9 开始,这些平台默认禁用不安全的 HTTP 连接。

只需添加

<uses-permission android:name="android.permission.INTERNET" />

    <application
      android:usesCleartextTraffic="true"

在您的 AndroidManifest.xml 中,然后将 fetch URL domain (localhost)替换为您的 IP 地址,

const dataRaw = await fetch('http://192.168.8.102:4000');

在我的情况下,它对https请求也显示相同。

重新安装应用程序解决了它。

  1. 在 AndroidManifest.xml 中添加android:usesCleartextTraffic="true"行。

  2. 从您的 android 文件夹中删除所有调试文件夹。

这不是答案,而是选项。 我切换到https://github.com/joltup/rn-fetch-blob它适用于表单数据和文件

解决方法是简单更新nodejs版本14或更高

"dependencies": {"react": "17.0.2","react-native": "0.66.1"},我在Android 模拟器上遇到了这个问题。

  1. 在 AndroidManifest.xml 中添加这些代码行

     <application .... .... android:usesCleartextTraffic="true">
  2. 然后,尝试在真实的物理设备而不是模拟器中运行您的代码,
    在物理设备上运行 - 连接你的 USB 并尝试运行 npx react-native run-android

修复TypeError: Network request failed

在 react-native 0.63.2(我正在测试)或更高版本中,如果只是使用fetch将文件上传到 http(不是 https)服务器,会遇到TypeError: Network request failed

在这里,我使用axios @0.27.2 作为在 Android 手机上运行的客户端成功将文件上传到react-native-file-server作为在另一部 Android 手机上运行的服务器。

客户端需要编辑JAVA和JS代码,服务器不需要编辑JAVA代码。

使用调试版本,必须注释掉此文件 android/app/src/debug/java/com/YOUR_PACKAGE_NAME/ReactNativeFlipper.java 中的第 43 行

38      NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
39      NetworkingModule.setCustomClientBuilder(
40          new NetworkingModule.CustomClientBuilder() {
41            @Override
42            public void apply(OkHttpClient.Builder builder) {
43      //        builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
44            }
45          });
46      client.addPlugin(networkFlipperPlugin);

也许还需要在<application of android/app/src/main/AndroidManifest.xml下添加android:usesCleartextTraffic="true" ,在我的测试中,调试和发布版本都不需要。

  onFileSelected = async (file) => {
    // API ref to the server side BE code `addWebServerFilter("/api/uploadtodir", new WebServerFilter()`
    // https://github.com/flyskywhy/react-native-file-server/blob/1034a33dd6d8b0999705927ad78368ca1a639add/android/src/main/java/webserver/WebServer.java#L356
    // could not be 'localhost' but IP address
    const serverUploadApi = 'http://192.168.1.123:8080/api/uploadtodir';

    // the folder on server where file will be uploaded to, could be e.g. '/storage/emulated/0/Download'
    const serverFolder = '/storage/emulated/0/FileServerUpload';

    const fileToUpload = {
      // if want to upload and rename, it can be `name: 'foo.bar'`, but can not be 'foo'
      // only if your server upload code support file name without type, on our server
      // https://github.com/flyskywhy/react-native-file-server/blob/1034a33dd6d8b0999705927ad78368ca1a639add/android/src/main/java/webserver/WebServer.java#L372
      // will cause java.lang.StringIndexOutOfBoundsException in substring()
      name: file.name,

      // type is necessary in Android, it can be 'image/jpeg' or 'foo/bar', but can not be
      // 'foo', 'foo/', '/foo' or undefined, otherwise will cause `[AxiosError: Network Error]`
      type: 'a/b',

      uri: Platform.OS === 'android' ? file.uri : file.uri.replace('file://', ''),
    };

    const form = new FormData();
    form.append('path', serverFolder);
    form.append('uploadfile', fileToUpload);

    // ref to the server side FE code `this.axios.post("/api/uploadtodir", parms, config)`
    // https://github.com/flyskywhy/react-native-file-server/blob/1034a33dd6d8b0999705927ad78368ca1a639add/fileserverwebdoc/src/views/Manage.vue#L411
    let res = await axios.post(serverUploadApi, form, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
      onUploadProgress: function (progressEvent) {
        console.warn(progressEvent);
      },
    });

    // ref to the server side BE code `return newFixedLengthResponse("Suss");`
    // https://github.com/flyskywhy/react-native-file-server/blob/1034a33dd6d8b0999705927ad78368ca1a639add/android/src/main/java/webserver/WebServer.java#L380
    if (res.data === 'Suss') {
      console.warn('Upload Successful');
    } else if (res.data === 'fail') {
      console.warn('Upload Failed');
    }
  };

如果您要连接到 HTTPS 网站,请检查您的模拟器的互联网连接。 (就我而言,我正在使用通过 USB 连接的手机模拟器,并且它的互联网处于离线状态)

为了找到根本问题,我使用XMLHttpRequest而不是fetch ,并且在发送请求后抛出了一个异常,并带有告诉实际问题HTTP_1_1_REQUIRED的消息。

显然反应原生(在我的情况下是 v0.59) fetch 只支持 http/1.1 然后我在网络服务器上禁用了 http/2。

如果您的网络服务器是 IIS 看看这个问题的答案

希望这会有所帮助。

React-native Expo 和 Node Express 后端有同样的问题。 问题是关于模拟器本地主机和服务器本地主机之间的冲突。 您的后端服务器可能在 127.0.0.1:8000 上运行,但模拟器无法找到它。

在终端中使用命令“ipconfig”找到您的 IPv4 地址。 例如,它将是 192.138.1.40

之后将其放入您的 fetch ('http://192.138.1.40:8080/')。 同样重要的是 - 使用相同的主机和端口运行您的后端服务器。 以 Node Express 为例:

app.listen(8080, () => console.log("服务器正在运行!"))

android 模拟器的一段时间冷启动工作正常。

只是您在 Fetch 中有更改....

fetch('http://facebook.github.io/react-native/movies.json')
    .then((response) => response.json())
    .then((responseJson) => {
        /*return responseJson.movies; */
        alert("result:"+JSON.stringify(responseJson))
        this.setState({
            dataSource:this.state.dataSource.cloneWithRows(responseJson)
        })
     }).catch((error) => {
         console.error(error);
     });

对于 Android 设备,转到您的项目根文件夹并运行以下命令:

adb reverse tcp:[your_own_server_port] tcp:[your_own_server_port]

例如, adb reverse tcp:8088 tcp:8088

这将使您的物理设备(即 Android 手机)监听在您的开发机器(即您的计算机)上运行的 localhost 服务器,地址为http://localhost:[your_own_server_port]

之后,您可以在 react-native fetch( ) 调用中直接使用http:localhost:[your_port] /your_api

对于 android,将 android:networkSecurityConfig="@xml/network_security_config" 添加到 AndroidManifest.xml 中的应用程序标记中,如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest ... >
        <application android:networkSecurityConfig="@xml/network_security_config"
                        ... >
            ...
        </application>
    </manifest>

network_security_config.xml 文件内容:

<?xml version='1.0' encoding='utf-8'?>
<network-security-config>
<debug-overrides>
    <trust-anchors>
        <!-- Trust user added CAs while debuggable only -->
        <certificates src="user" />
    </trust-anchors>
</debug-overrides>

<!-- let application to use http request -->
<base-config cleartextTrafficPermitted="true" />
</network-security-config>

就我而言,Android 模拟器没有连接到 Wi-Fi。

请参阅此处Android Studio - Android Emulator Wifi Connected with No Internet

例:

return fetch('http://<your ip>')
  .then((response) => response.json())
  .then((responseJson) => {
    console.log(responseJson)
  })
  .catch((error) => {
    console.error(error);
  });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM