问题

如标题,我想向我的后端发送一个请求,但是提示网络错误。但是并不是对所有的url(不同域名或者ip地址)都是提示错误,有部分url能够成功获取数据。已知使用的后台都是正确可用的。

axios
      .get('http://xxx.xxx.xxx.xxx:3000/api/...', {
        headers: {
          Accept:
          'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
          'Content-Type': 'application/json',
          'user-agent':
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/',
          'Access-Control-Allow-Origin': '*', // 允许跨域
        },
      })
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      });

问题分析

看了很多中文答案,但是都不凑效,那么就只有对问题进行分析了

  1. 首先无论是控制台还是flipper这样的debug工具,都未详细说明为什么网络请求失败,所以不清除具体缘由。
  2. 利用flipper的网络检测工具,发现这些错误请求压根就没有发送出去,网络请求一栏中全部都是空白,所以应该是RN把这个请求拦截了,与后端服务器无关。
  3. http和https测试后均表示可以成功发送部分请求,说明错误与协议无关。
  4. 无论使用ip地址还是域名作为url,都有成功发送请求的实例,说明与用ip地址或者域名是无关的。
  5. 当然fetch和axios都同时成功或者失败,那么自然说明与请求的方法无关。
  6. 什么请求头啊、跨域什么都也弄了的,所以与这个无关

这就很神奇了,太神奇了!这是个什么缘由。只有从stackoverflow之类的地方看看国外大神了,他们也说到了一个可能原因;

  1. ios中仅支持https,不过android没有这个问题。过!
  2. 没有设置<uses-permission android:name="android.permission.INTERNET" />
  3. 没有设置form类型
  4. 等等都没用实际作用的

后面看到github相关issue发现可能与ca证书有关,我的后端没有备案只用使用ip地址访问,同时为了安全我又申请了zeroSSL的相关证书,可能就是与ca证书不被这个安卓识别的原因,但是链接被拒绝的原因。

解决问题

没有证书,我就给它证书。参考相关内容(需要翻墙)对ca进行设置。

1.首先在android/app/src/main/AndroidManifest.xml中进行修改

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

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

    <application 
      android:usesCleartextTraffic="true"

<-- 增加下面一行,标志出安全文件的xml位置 -->
      android:networkSecurityConfig="@xml/network_security_config"

      android:name=".MainApplication"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:allowBackup="false"
      android:theme="@style/AppTheme">
      <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
       android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
        android:launchMode="singleTask"
        android:windowSoftInputMode="adjustResize"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
      </activity>
    </application>
</manifest>

2.然后android/app/src/main/res/xml/network_security_config.xml(没有就创建一个)中写下ca的相关安全设置

<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <!-- 信任系统默认的CAs -->
<!--            <certificates src="system" />-->
            <!-- 信任用户安装的CAs -->
            <certificates src="@raw/certificate" />
        </trust-anchors>
    </base-config>
</network-security-config>

3.然后进入到android/app/src/main/res/raw文件夹(没有就创建),把目标服务器的certificate.pem或者certificate.crt证书放到这个文件夹

4.最后npm run android命令重新构建一下就好了。

当然上述的文件夹啊、文件位置什么的都可以自己决定,然后就能解决问题了!

  参考

参考c网络安全配置  |  App quality  |  Android Developers

javascript - React Native fetch() Network Request Failed - Stack OverflowHTTP Fetch fails with "TypeError: Network request failed" => Resolved · Issue #32931 · facebook/react-native · GitHub
HTTP Fetch fails with "TypeError: Network request failed" => Resolved · Issue #32931 · facebook/react-native · GitHub
 

另一个小问题

开发过程中需要访问localhost:3000后端,但是也会提示上面类似错误,但是实际上是因为虚拟机与本地电脑桥接问题

解决方案如下

  1. 利用"adb reverse tcp:3000 tcp:3000",将两个端口映射一下
  2. 再就是比较简单的,用10.0.2.2这个ip地址替代localhost就好了
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐