iOS 模拟器 运行 Capacitor 转换的网络应用程序似乎无法从本地开发服务器一致地检索数据

iOS simulator running Capacitor converted web app can't seem to retrieve data consistently from local development server

我在 node.js 中开发了一个网络应用程序,现在我开始使用 capacitorjs 从中创建一个移动应用程序。在 Web 应用程序中,我使用把手来参数化视图,然后使用 res.render() 为它们提供服务。使用 capacitorjs,似乎继续的方法是预编译视图(模板和部分),然后只需 AJAX 调用服务器以获取数据,然后使用 [=65= 创建 html ](数据)等。到目前为止一切顺利!

对于开发阶段,我在本地机器上有 api 服务器 运行ning。我一直在本地机器上使用自签名 SSL 证书,在过去的几个月里,它没有给我带来任何麻烦,之前我已经将我自己的证书颁发机构的证书添加到我的 MacOS 机器上的钥匙串中,并且在接受了开发之后服务器在浏览器中的自签名证书。使用 capacitorjs,直接在 www 中组织网络资产并创建 index.html 文件后,我开始使用 iOS 创建移动应用程序的过程使用:

npx cap add iOS

open ios/App/App.xcworkspace

我发现 iOS 模拟器不喜欢使用自签名证书从 https 服务器访问任何内容,尽管我已将我的证书颁发机构的证书添加到设备的设置->常规->关于 -> 证书信任设置并启用对根证书的完全信任(包含我的证书颁发机构证书的列表)。在为此苦苦挣扎了一段时间之后,我决定 运行 我的开发服务器作为 http,至少在最初是为了让事情顺利进行。它仍然没有工作,我认为这是因为 CORS,所以我将这个中间件添加到我的应用程序堆栈中:

import cors from 'cors';
var corsOptions = {
  origin: 'capacitor://localhost',
  optionsSuccessStatus: 200
}
app.use(cors(corsOptions));

这似乎有效,尽管转瞬即逝!这是我发出的一个简单的 AJAX 请求,用于测试我是否可以从开发服务器检索数据:

  document.getElementById('test').addEventListener('click', event => {
    console.log('test clicked');
    console.log(`apiServer: ${apiServer}`);

    $.ajax({
      url: `${apiServer}/api/test`,
      type: 'get',
      data: {},
      success: function (data) {
        console.log(`/api/test`, data)
      },
      error: function (err) {
        console.log(`error: `, err);
      }
    });

  });

在服务器上,路由如下所示:

router.get('/api/test', function(req, res, next) {
  res.send({
    title: 'API Test',
    apiResponse: 'success',
  });
});

启动 iOS 模拟器后,我通过“测试”按钮进入应用程序的主屏幕。当我第一次点击它时,它工作正常。日志显示如下:

⚡️ [log] - apiServer: http://192.168.1.101:3000

⚡️ [log] - /api/test {"title":"API Test","apiResponse":"success"}

然而,随后的点击出现错误:

⚡️ [log] - apiServer: http://192.168.1.101:3000

⚡️ [log] - error: {"readyState":0,"status":0,"statusText":"error"}

我不知道为什么。我添加了第二个 API 测试路线并在第二次点击时点击它,但仍然出现相同的错误。也就是说,第一个 GET 请求有效,后续的请求无效。在服务器上,我可以看到它对第一个 GET 请求以状态 200 响应,有时对第二个 GET 请求也以状态 200 响应(无论我点击相同的测试路由还是两条不同的路由),但是,在 iOS 模拟器只有第一次点击获取数据,第二次导致错误。随后的 GET 请求(第三次,第四次,. . )没有被服务器注册,模拟器继续给出同样的错误:

⚡️ [log] - apiServer: http://192.168.1.101:3000

⚡️ [log] - error: {"readyState":0,"status":0,"statusText":"error"}

我被卡住了,需要帮助。感谢阅读!

更新:我已经按照 Stack Overflow 和 Apple Developer Forum 上各种讨论的建议尝试了各种应用程序传输安全设置,包括:

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

并且,这些设置的各种组合:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key><true/>
    <key>NSAllowsArbitraryLoadsInWebContent</key><true/>
    <key>NSAllowsLocalNetworking</key><true/>
    <key>NSExceptionDomains</key>
    <dict>
       <key>192.168.1.101:3000</key>
       <dict>
            <key>NSIncludesSubdomains</key><true/>
            <key>NSExceptionAllowsInsecureHTTPLoads</key<true/>
            <key>NSExceptionRequiresForwardSecrecy</key><true/>
       </dict>
    </dict>
</dict>

但似乎无济于事。令人惊讶的是,如果我将 NSAllowsArbitraryLoads 设置为 NO,并删除 ATS 中的所有其他键,行为仍然相同。也就是说,第一个 AJAX 请求成功,而随后的请求不成功,并给出与之前相同的错误。 Info.plist 中的 ATS 设置似乎无关紧要!

这似乎不是访问外部 API 服务器的权限问题。我在函数 makeRequest() 中结束了 AJAX 调用,并尝试使用 setInterval():

重复执行它
setInterval(() => { makeRequest(); }, 2000);

这有效:对 API 的重复 GET 请求(无论是在我位于 http://192.168.1.101:3000 or on an externally available public server such as https://emojihub.herokuapp.com/api/random 的本地服务器上)现在都成功了。因此,当单击按钮时,似乎有什么东西干扰了 AJAX 请求:从来没有第一次是模拟器加载应用程序,而是随后的几次。然后我将 event.preventDefault() 添加到按钮事件侦听器的顶部,这解决了问题。