【问题标题】:Flutter web integration test CORS XMLHttpRequest errorFlutter web集成测试CORS XMLHttpRequest报错
【发布时间】:2021-08-15 04:25:49
【问题描述】:

我正在本地为 web 运行 Flutter 集成测试。这是一个集成测试示例,我唯一想做的就是按下按钮,ping https://google.com,然后在收到响应后完成。

当我在本地运行此集成测试时,我收到 XMLHttpRequest 错误。这可能是一个 CORS 错误,虽然我不确定是不是这样。

如何在集成测试中为不属于我的网站发出 HTTP 请求?

颤振版本:

Flutter 2.2.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision b22742018b (12 days ago) • 2021-05-14 19:12:57 -0700
Engine • revision a9d88a4d18
Tools • Dart 2.13.0

错误:

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞═════════════════
The following ClientException was thrown running a test:
XMLHttpRequest error.

When the exception was thrown, this was the stack:
dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 909:28 get current
...

【问题讨论】:

  • 让我们假设错误是由 CORS 引起的 - 那实际上会很棒。因为这意味着集成测试模拟 真实 行为,而不是盲目地忽略 CORS 策略,这在真实浏览器上永远不会发生。 “如何从集成测试中向我不拥有的网站发出 HTTP 请求?” → 如果该服务器的 CORS 策略禁止您的请求,您为什么希望它在 web(使用 CORS 策略)集成测试中工作?
  • 退后一步:我认为您应该从找出实际错误开始。我的第一个猜测不是 CORS,而是 HTTP 请求被TestWidgetsFlutterBinding (故意)阻止的事实。您可以使用HttpOverrides 覆盖它(请参阅github.com/creativecreatorormaybenot/funvas/blob/…)。如果您已经这样做了,我认为我们将不得不确定实际错误。如果是 CORS,则意味着您的请求有过错,因为您不被允许(根据政策)。
  • 我尝试将 httpOverrides 设置为 null,但发生了同样的错误。顺便说一句,如果我在 Android 模拟器上运行完全相同的集成测试,它工作得很好。它可以向 Google 发出网络请求并获得响应。
  • 我明白了,我认为您使用的是 integration_test 而不是 flutter_test。我会写一个答案:)

标签: flutter http testing cors flutter-web


【解决方案1】:

为什么会这样?

正如您所说,您正在尝试 ping,即在 Web 集成测试中向https://www.google.com 发出 HTTP 请求

在网络上运行任何东西时,网络安全适用。在 Web 安全模型中,安全性由浏览器引擎使用Same-origin policy 强制执行,这实质上涉及浏览器引擎阻止前端 JavaScript 访问来自跨域请求的响应。但是这种阻塞可以通过使用Cross-Origin Resource Sharing (CORS) 来克服,这是服务器告诉浏览器它们明确允许跨域访问的一种方式。
这通常使用Access-Control-Allow-Origin header 完成。

由于您的集成测试将始终在 localhost 上运行,任何外部 HTTP 请求都将是跨域的。

CORS 示例

让我们检查一下https://www.google.com,这是您的示例:

如您所见,您看不到它。对了,google.com没有发送Access-Control-Allow-Origin响应头。

→ 这意味着根据网络安全,您不允许从其他域(跨域)发出此请求

它和网络集成测试有什么关系吗?

现在,问题可能会出现“为什么相同的集成测试可以在移动设备上运行”。
是的,这是一个很好的问题,答案很简单。 CORS 政策只存在于网络上,即它们实际上只存在于浏览器内部。这是因为任何人都可以(本质上)在网络上注入任何代码。但是,在移动设备上,请求是安全的,这就是为什么您可以提出任何您想要的请求 - 在网络上,您不能。


Flutter Web 集成测试将在 Chrome 实例中运行,这很有意义。集成测试的重点是模拟真实行为,即查看组件是否协同工作,对于 Flutter 的integration_testing(在某种程度上更接近自动化 e2e 测试),这意味着测试应用程序是否能够成功运行在网络平台上。

→ 想一想,用这种方式进行集成测试也是最有意义的,因为同样的请求在真实的 Web 应用中也会失败

和Flutter有关系吗?

,绝对不是!事实上,没有框架,没有办法避免这种情况,因为它是有意的。 Web 平台一般都有这些安全策略,您必须处理它们。

这意味着没有网络应用可以向https://www.google.com 发出 HTTP 请求(截至 2021 年 5 月 27 日),除非它实际上在同一个域上运行。


我们可以使用 Chrome 的 JS 控制台快速将其可视化,并从控制台 ping https://www.google.com,一次是在随机网站上,一次是在 Chrome 上。

如您所见,请求甚至在 JS 控制台中失败。您可能认为控制台具有特权,但即使在那里请求也会失败除非您位于同一来源(左侧)。
请注意,不同的策略/标头在错误信息。这将根据所使用的浏览器而有所不同,并且与参数/理解无关。

解决方案

既然我们已经确定这实际上是预期的行为,那么我们如何正确地处理这个问题,如何让它发挥作用?

使用正确的资源

您的测试失败,因为它应该失败。在 Web 上,您无法访问所需的域。但是,如果您使用实际允许的方法呢?

当然,实际上有许多站点确实允许 CORS,即允许从外部源访问。在这里您可以看到https://i.imgur.com/MQdD3lg.png 的响应标头:

Imgur 提供用于共享的图像,因此他们在逻辑上希望允许将这些图像嵌入到任何网站或网络应用程序中。这就是为什么你可以看到:

access-control-allow-origin: *

这意味着可以从任何地方、任何域请求图像。

→ 我建议您在集成测试中使用它来 ping ;)

这不是错误

为了强调这是 100% 预期的行为,我们需要回答这个问题通常如何解决的问题。

好吧,如果您要托管资源,您将是设置响应标头的人。出于调试测试目的所做的是指定允许访问的localhost端口。

添加 CORS 标头

例如如果要运行本地调试和测试,则需要指定要运行的端口。在 Flutter 中,这是通过 --web-port 参数完成的。您可以使用 --web-port 4200localhost:4200 上运行测试。
现在,您需要将此 localhost 端口添加到响应标头中允许的来源。可以在this Google Cloud article 中找到如何执行此操作的示例。

在 Chrome 中禁用网络安全

不推荐做什么(因为它与您的集成测试应该涵盖的真实场景不同)是一种完全禁用网络安全的方法。如果您这样做,您可以忽略所有网络安全策略并运行您想要的任何请求。

由于 Flutter 网络集成测试是在 Chrome 上运行的,因此请在 Chrome 设备上使用you can use --disable-web-security 来禁用网络安全。

【讨论】:

  • 我接受了这个答案。我已经确认向开放 API 发送 HTTP 请求是可行的,所以问题一定是 CORS 限制。这是一个不幸的限制,但如果所有网络都以同样的方式受到限制,那么它就是这样。
猜你喜欢
  • 1970-01-01
  • 2021-08-22
  • 2020-01-05
  • 2022-11-16
  • 2020-05-28
  • 1970-01-01
  • 1970-01-01
  • 2022-11-06
  • 2022-01-07
相关资源
最近更新 更多