React Native fetch() 网络请求失败

228

当我使用 react-native init (RN版本0.29.1) 创建一个全新的项目,然后在渲染方法中使用 fetch 请求公共的 Facebook Demo 电影 API 时,会抛出一个 Network Request Failed 错误。这里有一个非常无用的堆栈跟踪信息,而且我无法在 Chrome 控制台中调试网络请求。下面是我发送的 fetch 代码:

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

1
我不确定。我正在使用iOS模拟器,我以为它使用的是我的电脑的网络连接。 - Alek Hurst
8
http 更改为 https(如果可能)很可能会解决您的问题。 - Nick Zuber
2
我曾经在未将服务器绑定到该IP的情况下使用了ifconfig中的IP 192.168.1.25:3000,正确的做法是使用rails server --binding=192.168.1.25 --port=3000进行绑定。 - Fabrizio Bertoglio
1
这些答案都不能让你捕获错误。 - Andrew Koster
2
是的,我尝试过所有方法,但都没有成功。 - Vigneswaran A
显示剩余4条评论
41个回答

189

问题在于iOS默认情况下不允许HTTP请求,只允许HTTPS请求。如果您想启用HTTP请求,请将以下内容添加到您的info.plist文件中:

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

2
这个答案对我很有用。对于其他初学者,这个文件(info.plist)也可以通过xcode进行编辑:https://dev59.com/R1oT5IYBdhLWcg3w4CiO#38219454 - Marklar
140
安卓怎么样?我在安卓上也遇到了同样的问题。 - Vijay Sharma
33
没错,有人知道安卓的答案吗? - Zach Cook
1
当我将其放在ios/build/info.plist并运行时: react-native run-iosios/build/info.plist被覆盖并退出了我的更改我该怎么办? - Jorge Wander Santana Ureña
3
我已经在AndroidManifest.xml中授予了INTERNET权限,但仍然遇到了这个问题。有什么办法可以解决吗?如果我还有头发的话,我可能都要把它们都拔光了... 呜呜 - masud_moni
显示剩余11条评论

71

我之前使用的地址是 localhost,这显然是错误的。将其替换为服务器的IP地址(在模拟器所在的网络中),它就可以正常工作了。

编辑

在Android模拟器中,开发计算机的地址是10.0.2.2。更多解释请看这里

对于Genymotion,地址是10.0.3.2。更多信息请参见这里


5
谢谢。我可能看到的唯一适用于安卓的响应。使用我的网络IP地址,它可以工作。例如:http://<模拟器连接到的网络IP>:<提供API的端口>/api/tasks - Brandon Benefield
3
别忘了加上"http://",否则它将无法工作...我在想为什么在Postman上可以工作,但是用fetch却不行。 - Moumou
@Mahmoodvcs,你的回答帮了我很多,非常感谢。我正在使用安卓模拟器。 - Pena Pintada
是的,这个可以工作。将“localhost”更改为您的开发计算机的实际IP地址。 - Daggie Blanqx - Douglas Mwangi

67

不建议允许所有域名的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>

54
安卓怎么样? - arisalexis
4
我的React Native应用程序中似乎有多个目录中的多个info.plist文件。有任何想法哪个文件夹包含正确的更改文件? - Marklar
2
@Marklar [你的项目文件夹名称]/ios/[你的项目文件夹名称]/Info.plist - Stich
如果我在这里使用我的本地主机:<key>yourserver.com</key>,当准备好发布时,我需要更改它,对吗? - Anayo Oleru

41
我在Android 9上遇到了同样的问题,因为使用了“http”,只需在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>


4
谢谢,这对我也有效。这是因为Android在API 28上默认不再支持HTTP了。 - Gui Herzog

23

因为我们正在上传一个文件,RN filePicker没有提供正确的MIME类型,只给了我们“image”这个类型。我们需要将它更改为“image/jpg”才能使fetch正常工作。

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

@JohhanSantana 你可能可以查看原始图像数据并从开头获取它,但对我来说,React Native选择器只返回了“image”。 - NickJ

16

如果您正在使用 localhost,请将其更改为:

从:http://localhost:3030

到:http://10.0.2.2:3030


你如何将它从localhost更改为你的IP地址? - Olumide
@Olumide 只需更改您的 URL。谢谢,这个方法可行。 - Necmettin Sargın
我把它从http://localhost:3000 改成了 http://192.168.1.256:3000,然后它就起作用了。 - Sudarshan

16

我在安卓上遇到了相同的问题,但我成功找到了解决方案。从API Level 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 0.59和目标SDK 28上运行良好 :) - sameera madushan
如果我甚至有res/xml/...路径,那就太好了...我应该添加吗?会有什么区别吗? 使用RN - 0.72,sdk > 30 - undefined

15

针对Android用户:

  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,除非该应用程序显式地选择了信任。这种默认的安全设置减少了应用程序的攻击面,并鼓励对网络和基于文件的应用程序数据进行一致处理。


12

没什么变化:/ - undefined

11

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

Android 7.0存在一个 bug,详情请见这里。Vicky Chijwani提出的解决方法如下:

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接