Flutter web 集成测试 CORS XMLHttpRequest 报错

Flutter web integration test CORS XMLHttpRequest error

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

当我 运行 在本地进行此集成测试时,收到 XMLHttpRequest 错误。这可能是一个 CORS 错误,但我不确定情况是否如此。

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

Flutter 版本:

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
...

为什么会这样?

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

当运行网络上的任何内容时,网络安全适用。在 Web 安全模型中,安全由使用 Same-origin policy, which essentially involves browser engines blocking access by frontend JavaScript to responses from cross-origin requests. But that blocking can be overcome by using Cross-Origin Resource Sharing (CORS) 的浏览器引擎强制执行,这是服务器告诉浏览器他们明确允许 cross-origin 访问的一种方式。
这通常使用 Access-Control-Allow-Origin header.

由于您的集成测试总是 运行 localhost任何外部 HTTP 请求 将是 cross-origin.

CORS 示例

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

如你所见,如你所见。是的,google.com.

没有发送 Access-Control-Allow-Origin 响应 header

→ 这意味着 根据网络安全,您不允许从不同的域发出此请求(cross-origin).

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

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


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

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

跟Flutter有关系吗?

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

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


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

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

解决方案

现在我们已经确定这实际上是预期的行为,我们如何正确地处理它,我们如何让它发挥作用?

使用正确的资源

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

当然,有很多网站确实允许 CORS,即允许从外部源访问。在这里您可以看到 https://i.imgur.com/MQdD3lg.png:

的响应 headers

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

access-control-allow-origin: *

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

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

这不是错误

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

嗯,如果您托管资源,您将是设置响应 headers 的人。 调试测试目的是指定允许访问的本地主机端口。

添加 CORS headers

例如如果你想运行你的本地调试和测试,你会想指定一个端口到运行上。在 Flutter 中,这是通过 --web-port 参数完成的。您可以 运行 使用 --web-port 4200localhost:4200 上进行测试。
现在,您需要将此本地主机端口添加到响应 header 中允许的来源。在 this Google Cloud article.

中可以找到如何执行此操作的示例

在 Chrome

中禁用网络安全

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

由于 Flutter 网络集成测试在 Chrome 上进行 运行,在 Chrome 设备上进行 you can use --disable-web-security 以禁用网络安全。